-
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.
HHH-18901 The entity enhancement process made defensive against concu…
…rrent enhancement In particular, made it defensive against concurrent enhancement of the same resource, or of resources that might trigger loading symbols which another thread is re-processing.
- Loading branch information
Showing
10 changed files
with
276 additions
and
50 deletions.
There are no files selected for viewing
68 changes: 68 additions & 0 deletions
68
...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,68 @@ | ||
/* | ||
* 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; | ||
|
||
/** | ||
* 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} | ||
*/ | ||
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(); | ||
} | ||
|
||
} |
57 changes: 57 additions & 0 deletions
57
...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,57 @@ | ||
/* | ||
* 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 static final CorePrefixFilter DEFAULT_INSTANCE = new CorePrefixFilter(); | ||
|
||
/** | ||
* Do not invoke: use DEFAULT_INSTANCE | ||
*/ | ||
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 ); | ||
} | ||
|
||
public 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
Oops, something went wrong.