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;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.boot.registry.StandardServiceInitiator;
|
||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||
import org.hibernate.bytecode.spi.BytecodeProvider;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
||||
|
||||
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> {
|
||||
|
||||
/**
|
||||
* @deprecated Register a {@link BytecodeProvider} through Java {@linkplain java.util.ServiceLoader services}.
|
||||
*/
|
||||
@Deprecated( forRemoval = true )
|
||||
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";
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated with no replacement
|
||||
*/
|
||||
@Deprecated( forRemoval = true )
|
||||
public static final String BYTECODE_PROVIDER_NAME_DEFAULT = BYTECODE_PROVIDER_NAME_BYTEBUDDY;
|
||||
|
||||
/**
|
||||
|
@ -32,8 +49,9 @@ public final class BytecodeProviderInitiator implements StandardServiceInitiator
|
|||
|
||||
@Override
|
||||
public BytecodeProvider initiateService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
|
||||
String provider = ConfigurationHelper.getString( BYTECODE_PROVIDER, configurationValues, BYTECODE_PROVIDER_NAME_DEFAULT );
|
||||
return buildBytecodeProvider( provider );
|
||||
final ClassLoaderService classLoaderService = castNonNull( registry.getService( ClassLoaderService.class ) );
|
||||
final Collection<BytecodeProvider> bytecodeProviders = classLoaderService.loadJavaServices( BytecodeProvider.class );
|
||||
return getBytecodeProvider( bytecodeProviders );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,12 +61,26 @@ public final class BytecodeProviderInitiator implements StandardServiceInitiator
|
|||
|
||||
@Internal
|
||||
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
|
||||
public static BytecodeProvider buildBytecodeProvider(String providerName) {
|
||||
|
||||
CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, BytecodeProviderInitiator.class.getName() );
|
||||
LOG.bytecodeProvider( providerName );
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Map;
|
|||
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
|
||||
import org.hibernate.bytecode.enhance.spi.Enhancer;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.service.JavaServiceLoadable;
|
||||
import org.hibernate.service.Service;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -25,6 +26,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@JavaServiceLoadable
|
||||
public interface BytecodeProvider extends Service {
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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";
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,6 +17,8 @@ import org.hibernate.proxy.AbstractSerializableProxy;
|
|||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.type.CompositeType;
|
||||
|
||||
import static org.hibernate.bytecode.internal.BytecodeProviderInitiator.buildDefaultBytecodeProvider;
|
||||
|
||||
public final class SerializableProxy extends AbstractSerializableProxy {
|
||||
private final Class<?> persistentClass;
|
||||
private final Class<?>[] interfaces;
|
||||
|
@ -30,6 +32,8 @@ public final class SerializableProxy extends AbstractSerializableProxy {
|
|||
|
||||
private final CompositeType componentIdType;
|
||||
|
||||
private static volatile BytecodeProviderImpl fallbackBytecodeProvider;
|
||||
|
||||
public SerializableProxy(
|
||||
String entityName,
|
||||
Class<?> persistentClass,
|
||||
|
@ -120,17 +124,27 @@ public final class SerializableProxy extends AbstractSerializableProxy {
|
|||
|
||||
private static SessionFactoryImplementor retrieveMatchingSessionFactory(final String sessionFactoryUuid, final String sessionFactoryName) {
|
||||
Objects.requireNonNull( sessionFactoryUuid );
|
||||
final SessionFactoryImplementor sessionFactory = SessionFactoryRegistry.INSTANCE.findSessionFactory( sessionFactoryUuid, sessionFactoryName );
|
||||
if ( sessionFactory != null ) {
|
||||
return sessionFactory;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException( "Could not identify any active SessionFactory having UUID " + sessionFactoryUuid );
|
||||
}
|
||||
return SessionFactoryRegistry.INSTANCE.findSessionFactory( sessionFactoryUuid, sessionFactoryName );
|
||||
}
|
||||
|
||||
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 ) {
|
||||
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." );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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