-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The entity enhancement process made defensive against concurrent enha…
…ncement of the same resources
- Loading branch information
Showing
8 changed files
with
246 additions
and
30 deletions.
There are no files selected for viewing
70 changes: 70 additions & 0 deletions
70
...re/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CoreCacheProvider.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 | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Hibernate, Relational Persistence for Idiomatic Java | ||
* | ||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. | ||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. | ||
*/ | ||
package org.hibernate.bytecode.enhance.internal.bytebuddy; | ||
|
||
import java.util.Objects; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.ConcurrentMap; | ||
|
||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.pool.TypePool; | ||
import net.bytebuddy.utility.nullability.MaybeNull; | ||
|
||
/** | ||
* A CacheProvider for ByteBuddy which is specifically designed for | ||
* our CoreTypePool: it ensures the cache content doesn't get tainted | ||
* by model types or anything else which isn't responsibility of this | ||
* particular type pool. | ||
* @implNote This cache instance shares the same @{@link CorePrefixFilter} | ||
* instance of the @{@link CoreTypePool} which is using it, and uses it | ||
* to guard writes into its internal caches. | ||
*/ | ||
class CoreCacheProvider implements TypePool.CacheProvider { | ||
|
||
private final ConcurrentMap<String, TypePool.Resolution> storage = new ConcurrentHashMap<>(); | ||
private final CorePrefixFilter acceptedPrefixes; | ||
|
||
CoreCacheProvider(final CorePrefixFilter acceptedPrefixes) { | ||
this.acceptedPrefixes = Objects.requireNonNull( acceptedPrefixes ); | ||
register( | ||
Object.class.getName(), | ||
new TypePool.Resolution.Simple( TypeDescription.ForLoadedType.of( Object.class ) ) | ||
); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
@MaybeNull | ||
public TypePool.Resolution find(final String name) { | ||
return storage.get( name ); | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public TypePool.Resolution register(String name, TypePool.Resolution resolution) { | ||
//Ensure we DO NOT cache anything from a non-core namespace, to not leak application specific code: | ||
if ( acceptedPrefixes.isCoreClassName( name ) ) { | ||
TypePool.Resolution cached = storage.putIfAbsent( name, resolution ); | ||
return cached == null | ||
? resolution | ||
: cached; | ||
} | ||
else { | ||
return resolution; | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public void clear() { | ||
storage.clear(); | ||
} | ||
|
||
} |
53 changes: 53 additions & 0 deletions
53
...ore/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CorePrefixFilter.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 | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* Hibernate, Relational Persistence for Idiomatic Java | ||
* | ||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. | ||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. | ||
*/ | ||
package org.hibernate.bytecode.enhance.internal.bytebuddy; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* We differentiate between core classes and application classes during symbol | ||
* resolution for the purposes of entity enhancement. | ||
* The discriminator is the prefix of the fully qualified classname, for | ||
* example it could be package names. | ||
* The "core classes" don't have to be comprehensively defined: we want a small | ||
* set of prefixes for which we know with certainty that a)They won't be used | ||
* in application code (assuming people honour reasonable package name rules) | ||
* or any code that needs being enhanced. and b) frequently need to be looked up | ||
* during the enhancement process. | ||
* A great example is the "jakarta.persistence.Entity" annotation: we'll most likely | ||
* need to load it when doing any form of introspection on user's code, but we expect | ||
* the bytecode which represents the annotation to not be enhanced. | ||
* We then benefit from caching such representations of object types which are frequently | ||
* loaded; since caching end user code would lead to enhancement problems, it's best | ||
* to keep the list conservative when in doubt. | ||
* For example, don't consider all of {@code "org.hibernate."} prefixes as safe, as | ||
* that would include entities used during our own testsuite and entities defined by Envers. | ||
* | ||
*/ | ||
public final class CorePrefixFilter { | ||
|
||
private final String[] acceptedPrefixes; | ||
|
||
public CorePrefixFilter() { | ||
//By default optimise for jakarta annotations, java util collections, and Hibernate marker interfaces | ||
this("jakarta.", "java.", "org.hibernate.annotations.", "org.hibernate.bytecode.enhance.spi.", "org.hibernate.engine.spi."); | ||
} | ||
|
||
public CorePrefixFilter(final String... acceptedPrefixes) { | ||
this.acceptedPrefixes = Objects.requireNonNull( acceptedPrefixes ); | ||
} | ||
|
||
boolean isCoreClassName(final String name) { | ||
for ( String acceptedPrefix : this.acceptedPrefixes ) { | ||
if ( name.startsWith( acceptedPrefix ) ) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
} |
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
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
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
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
49 changes: 49 additions & 0 deletions
49
...re/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/SafeCacheProvider.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 | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* Hibernate, Relational Persistence for Idiomatic Java | ||
* | ||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. | ||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. | ||
*/ | ||
package org.hibernate.bytecode.enhance.internal.bytebuddy; | ||
|
||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.ConcurrentMap; | ||
|
||
import net.bytebuddy.pool.TypePool; | ||
|
||
/** | ||
* An implementation of @{@link net.bytebuddy.pool.TypePool.CacheProvider} which scopes | ||
* all state to the current thread, and allows to remove specific registrations. | ||
* The threadscoping is necessary to resolve a race condition happening during concurrent entity enhancement: | ||
* while one thread is resolving metadata about the entity which needs to be enhanced, other threads | ||
* might be working on the same operation (or a different entity which needs this one to be resolved) | ||
* and the resolution output - potentially shared across them - could be tainted as they do need | ||
* to occasionally work on different input because of the specific overrides managed via @{@link OverridingClassFileLocator}. | ||
*/ | ||
final class SafeCacheProvider implements TypePool.CacheProvider { | ||
|
||
private final ThreadLocal<ConcurrentMap<String, TypePool.Resolution>> delegate = ThreadLocal.withInitial( () -> new ConcurrentHashMap<>() ); | ||
|
||
@Override | ||
public TypePool.Resolution find(final String name) { | ||
return delegate.get().get( name ); | ||
} | ||
|
||
@Override | ||
public TypePool.Resolution register(final String name, final TypePool.Resolution resolution) { | ||
final TypePool.Resolution cached = delegate.get().putIfAbsent( name, resolution ); | ||
return cached == null | ||
? resolution | ||
: cached; | ||
} | ||
|
||
@Override | ||
public void clear() { | ||
delegate.get().clear(); | ||
delegate.remove(); | ||
} | ||
|
||
public TypePool.Resolution remove(final String name) { | ||
return delegate.get().remove( name ); | ||
} | ||
} |
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