HHH-17643 Load `BytecodeProvider` as a java service
Also allow `SerializableProxy` deserialization even when no session factory is available.
This commit is contained in:
parent
776c05cad7
commit
5587badf3f
|
@ -6,23 +6,40 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.bytecode.internal;
|
package org.hibernate.bytecode.internal;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
|
||||||
import org.hibernate.Internal;
|
import org.hibernate.Internal;
|
||||||
import org.hibernate.boot.registry.StandardServiceInitiator;
|
import org.hibernate.boot.registry.StandardServiceInitiator;
|
||||||
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.bytecode.spi.BytecodeProvider;
|
import org.hibernate.bytecode.spi.BytecodeProvider;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
|
||||||
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import static org.hibernate.cfg.BytecodeSettings.BYTECODE_PROVIDER;
|
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
|
||||||
|
|
||||||
public final class BytecodeProviderInitiator implements StandardServiceInitiator<BytecodeProvider> {
|
public final class BytecodeProviderInitiator implements StandardServiceInitiator<BytecodeProvider> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Register a {@link BytecodeProvider} through Java {@linkplain java.util.ServiceLoader services}.
|
||||||
|
*/
|
||||||
|
@Deprecated( forRemoval = true )
|
||||||
public static final String BYTECODE_PROVIDER_NAME_BYTEBUDDY = "bytebuddy";
|
public static final String BYTECODE_PROVIDER_NAME_BYTEBUDDY = "bytebuddy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Register a {@link BytecodeProvider} through Java {@linkplain java.util.ServiceLoader services}.
|
||||||
|
*/
|
||||||
|
@Deprecated( forRemoval = true )
|
||||||
public static final String BYTECODE_PROVIDER_NAME_NONE = "none";
|
public static final String BYTECODE_PROVIDER_NAME_NONE = "none";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Deprecated with no replacement
|
||||||
|
*/
|
||||||
|
@Deprecated( forRemoval = true )
|
||||||
public static final String BYTECODE_PROVIDER_NAME_DEFAULT = BYTECODE_PROVIDER_NAME_BYTEBUDDY;
|
public static final String BYTECODE_PROVIDER_NAME_DEFAULT = BYTECODE_PROVIDER_NAME_BYTEBUDDY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,8 +49,9 @@ public final class BytecodeProviderInitiator implements StandardServiceInitiator
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BytecodeProvider initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
|
public BytecodeProvider initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
|
||||||
String provider = ConfigurationHelper.getString( BYTECODE_PROVIDER, configurationValues, BYTECODE_PROVIDER_NAME_DEFAULT );
|
final ClassLoaderService classLoaderService = castNonNull( registry.getService( ClassLoaderService.class ) );
|
||||||
return buildBytecodeProvider( provider );
|
final Collection<BytecodeProvider> bytecodeProviders = classLoaderService.loadJavaServices( BytecodeProvider.class );
|
||||||
|
return getBytecodeProvider( bytecodeProviders );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,12 +61,26 @@ public final class BytecodeProviderInitiator implements StandardServiceInitiator
|
||||||
|
|
||||||
@Internal
|
@Internal
|
||||||
public static BytecodeProvider buildDefaultBytecodeProvider() {
|
public static BytecodeProvider buildDefaultBytecodeProvider() {
|
||||||
return buildBytecodeProvider( BYTECODE_PROVIDER_NAME_BYTEBUDDY );
|
return getBytecodeProvider( ServiceLoader.load( BytecodeProvider.class ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Internal
|
||||||
|
public static BytecodeProvider getBytecodeProvider(Iterable<BytecodeProvider> bytecodeProviders) {
|
||||||
|
final Iterator<BytecodeProvider> iterator = bytecodeProviders.iterator();
|
||||||
|
if ( !iterator.hasNext() ) {
|
||||||
|
// If no BytecodeProvider service is available, default to the "no-op" enhancer
|
||||||
|
return new org.hibernate.bytecode.internal.none.BytecodeProviderImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
final BytecodeProvider provider = iterator.next();
|
||||||
|
if ( iterator.hasNext() ) {
|
||||||
|
throw new IllegalStateException( "Found multiple BytecodeProvider service registrations, cannot determine which one to use" );
|
||||||
|
}
|
||||||
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Internal
|
@Internal
|
||||||
public static BytecodeProvider buildBytecodeProvider(String providerName) {
|
public static BytecodeProvider buildBytecodeProvider(String providerName) {
|
||||||
|
|
||||||
CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, BytecodeProviderInitiator.class.getName() );
|
CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, BytecodeProviderInitiator.class.getName() );
|
||||||
LOG.bytecodeProvider( providerName );
|
LOG.bytecodeProvider( providerName );
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Map;
|
||||||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||||
import org.hibernate.property.access.spi.PropertyAccess;
|
import org.hibernate.property.access.spi.PropertyAccess;
|
||||||
|
import org.hibernate.service.JavaServiceLoadable;
|
||||||
import org.hibernate.service.Service;
|
import org.hibernate.service.Service;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
@ -25,6 +26,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@JavaServiceLoadable
|
||||||
public interface BytecodeProvider extends Service {
|
public interface BytecodeProvider extends Service {
|
||||||
/**
|
/**
|
||||||
* Retrieve the specific factory for this provider capable of
|
* Retrieve the specific factory for this provider capable of
|
||||||
|
|
|
@ -20,7 +20,12 @@ public interface BytecodeSettings {
|
||||||
* At present only bytebuddy is supported, bytebuddy being the default since version 5.3.
|
* At present only bytebuddy is supported, bytebuddy being the default since version 5.3.
|
||||||
*
|
*
|
||||||
* @settingDefault {@code "bytebuddy"}
|
* @settingDefault {@code "bytebuddy"}
|
||||||
|
* @deprecated Will be removed, Hibernate ORM will use the BytecodeProvider implementation it finds on the
|
||||||
|
* classpath loading it via the standard ServiceLoader mechanism. Currently, there is only a single
|
||||||
|
* implementation which is included in Hibernate ORM, so it's not possible to override this.
|
||||||
|
* See HHH-17643
|
||||||
*/
|
*/
|
||||||
|
@Deprecated( forRemoval = true )
|
||||||
String BYTECODE_PROVIDER = "hibernate.bytecode.provider";
|
String BYTECODE_PROVIDER = "hibernate.bytecode.provider";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,6 +17,8 @@ import org.hibernate.proxy.AbstractSerializableProxy;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.type.CompositeType;
|
import org.hibernate.type.CompositeType;
|
||||||
|
|
||||||
|
import static org.hibernate.bytecode.internal.BytecodeProviderInitiator.buildDefaultBytecodeProvider;
|
||||||
|
|
||||||
public final class SerializableProxy extends AbstractSerializableProxy {
|
public final class SerializableProxy extends AbstractSerializableProxy {
|
||||||
private final Class<?> persistentClass;
|
private final Class<?> persistentClass;
|
||||||
private final Class<?>[] interfaces;
|
private final Class<?>[] interfaces;
|
||||||
|
@ -30,6 +32,8 @@ public final class SerializableProxy extends AbstractSerializableProxy {
|
||||||
|
|
||||||
private final CompositeType componentIdType;
|
private final CompositeType componentIdType;
|
||||||
|
|
||||||
|
private static volatile BytecodeProviderImpl fallbackBytecodeProvider;
|
||||||
|
|
||||||
public SerializableProxy(
|
public SerializableProxy(
|
||||||
String entityName,
|
String entityName,
|
||||||
Class<?> persistentClass,
|
Class<?> persistentClass,
|
||||||
|
@ -120,17 +124,27 @@ public final class SerializableProxy extends AbstractSerializableProxy {
|
||||||
|
|
||||||
private static SessionFactoryImplementor retrieveMatchingSessionFactory(final String sessionFactoryUuid, final String sessionFactoryName) {
|
private static SessionFactoryImplementor retrieveMatchingSessionFactory(final String sessionFactoryUuid, final String sessionFactoryName) {
|
||||||
Objects.requireNonNull( sessionFactoryUuid );
|
Objects.requireNonNull( sessionFactoryUuid );
|
||||||
final SessionFactoryImplementor sessionFactory = SessionFactoryRegistry.INSTANCE.findSessionFactory( sessionFactoryUuid, sessionFactoryName );
|
return SessionFactoryRegistry.INSTANCE.findSessionFactory( sessionFactoryUuid, sessionFactoryName );
|
||||||
if ( sessionFactory != null ) {
|
|
||||||
return sessionFactory;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException( "Could not identify any active SessionFactory having UUID " + sessionFactoryUuid );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BytecodeProviderImpl retrieveByteBuddyBytecodeProvider(final SessionFactoryImplementor sessionFactory) {
|
private static BytecodeProviderImpl retrieveByteBuddyBytecodeProvider(final SessionFactoryImplementor sessionFactory) {
|
||||||
final BytecodeProvider bytecodeProvider = sessionFactory.getServiceRegistry().getService( BytecodeProvider.class );
|
if ( sessionFactory == null ) {
|
||||||
|
// When the session factory is not available fallback to local bytecode provider
|
||||||
|
return getFallbackBytecodeProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
return castBytecodeProvider( sessionFactory.getServiceRegistry().getService( BytecodeProvider.class ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BytecodeProviderImpl getFallbackBytecodeProvider() {
|
||||||
|
BytecodeProviderImpl provider = fallbackBytecodeProvider;
|
||||||
|
if ( provider == null ) {
|
||||||
|
provider = fallbackBytecodeProvider = castBytecodeProvider( buildDefaultBytecodeProvider() );
|
||||||
|
}
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BytecodeProviderImpl castBytecodeProvider(BytecodeProvider bytecodeProvider) {
|
||||||
if ( bytecodeProvider instanceof BytecodeProviderImpl ) {
|
if ( bytecodeProvider instanceof BytecodeProviderImpl ) {
|
||||||
return (BytecodeProviderImpl) bytecodeProvider;
|
return (BytecodeProviderImpl) bytecodeProvider;
|
||||||
}
|
}
|
||||||
|
@ -138,5 +152,4 @@ public final class SerializableProxy extends AbstractSerializableProxy {
|
||||||
throw new IllegalStateException( "Unable to deserialize a SerializableProxy proxy: the bytecode provider is not ByteBuddy." );
|
throw new IllegalStateException( "Unable to deserialize a SerializableProxy proxy: the bytecode provider is not ByteBuddy." );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.serialization;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.cfg.Configuration;
|
||||||
|
import org.hibernate.internal.SessionFactoryRegistry;
|
||||||
|
import org.hibernate.internal.util.SerializationHelper;
|
||||||
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
|
|
||||||
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
|
import org.hibernate.testing.util.ServiceRegistryUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Marco Belladelli
|
||||||
|
*/
|
||||||
|
public class ProxySerializationNoSessionFactoryTest extends BaseUnitTestCase {
|
||||||
|
@Test
|
||||||
|
public void testUninitializedProxy() {
|
||||||
|
executeTest( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInitializedProxy() {
|
||||||
|
executeTest( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeTest(boolean initializeProxy) {
|
||||||
|
final Configuration cfg = new Configuration()
|
||||||
|
.setProperty( AvailableSettings.HBM2DDL_AUTO, "create-drop" )
|
||||||
|
.addAnnotatedClass( SimpleEntity.class )
|
||||||
|
.addAnnotatedClass( ChildEntity.class );
|
||||||
|
ServiceRegistryUtil.applySettings( cfg.getStandardServiceRegistryBuilder() );
|
||||||
|
final SimpleEntity parent;
|
||||||
|
try (final SessionFactory factory = cfg.buildSessionFactory()) {
|
||||||
|
doInHibernate( () -> factory, session -> {
|
||||||
|
final SimpleEntity entity = new SimpleEntity();
|
||||||
|
entity.setId( 1L );
|
||||||
|
entity.setName( "TheParent" );
|
||||||
|
session.persist( entity );
|
||||||
|
|
||||||
|
final ChildEntity child = new ChildEntity();
|
||||||
|
child.setId( 1L );
|
||||||
|
child.setParent( entity );
|
||||||
|
session.persist( child );
|
||||||
|
} );
|
||||||
|
|
||||||
|
parent = doInHibernate( () -> factory, session -> {
|
||||||
|
final ChildEntity childEntity = session.find( ChildEntity.class, 1L );
|
||||||
|
final SimpleEntity entity = childEntity.getParent();
|
||||||
|
if ( initializeProxy ) {
|
||||||
|
assertEquals( "TheParent",entity.getName() );
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
// The session factory is not available anymore
|
||||||
|
assertFalse( SessionFactoryRegistry.INSTANCE.hasRegistrations() );
|
||||||
|
|
||||||
|
assertTrue( parent instanceof HibernateProxy );
|
||||||
|
assertEquals( initializeProxy, Hibernate.isInitialized( parent ) );
|
||||||
|
|
||||||
|
// Serialization and deserialization should still work
|
||||||
|
final SimpleEntity clone = (SimpleEntity) SerializationHelper.clone( parent );
|
||||||
|
assertNotNull( clone );
|
||||||
|
assertEquals( parent.getId(), clone.getId() );
|
||||||
|
if ( initializeProxy ) {
|
||||||
|
assertEquals( parent.getName(), clone.getName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "SimpleEntity" )
|
||||||
|
static class SimpleEntity implements Serializable {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(final Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "ChildEntity" )
|
||||||
|
static class ChildEntity {
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne( fetch = FetchType.LAZY )
|
||||||
|
@JoinColumn
|
||||||
|
private SimpleEntity parent;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(final Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleEntity getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(SimpleEntity parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue