Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions orm/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
<dependency>
<groupId>com.google.googlejavaformat</groupId>
<artifactId>google-java-format</artifactId>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,152 @@
package org.hibernate.tool.internal.export.mapping;

import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import org.apache.commons.collections4.list.UnmodifiableList;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping;
import org.hibernate.boot.jaxb.hbm.transform.HbmXmlTransformer;
import org.hibernate.boot.jaxb.hbm.transform.UnsupportedFeatureHandling;
import org.hibernate.boot.jaxb.internal.MappingBinder;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl;
import org.hibernate.boot.jaxb.spi.Binding;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.JdbcSettings;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.api.export.Exporter;
import org.hibernate.tool.api.xml.XMLPrettyPrinter;
import org.hibernate.tool.internal.util.DummyDialect;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;

public class MappingExporter implements Exporter {

private static final Logger LOGGER = Logger.getLogger( MappingExporter.class.getName() );

private UnmodifiableList<File> hbmXmlFiles;
private boolean formatResult = true;

public void setHbmFiles(List<File> fileList) {
hbmXmlFiles = new UnmodifiableList<>( fileList );
}

public void setFormatResult(boolean formatResult) {
this.formatResult = formatResult;
}

@Override
public Properties getProperties() {
return null;
}

@Override
public void start() {
MappingBinder mappingBinder = createMappingBinder();
List<Binding<JaxbHbmHibernateMapping>> hbmMappings = getHbmMappings(mappingBinder);
performTransformation(hbmMappings, mappingBinder, createServiceRegistry(), formatResult);
}

private ServiceRegistry createServiceRegistry() {
StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder();
ssrb.clearSettings();
ssrb.applySetting(JdbcSettings.ALLOW_METADATA_ON_BOOT, false);
ssrb.applySetting(JdbcSettings.DIALECT, DummyDialect.class.getName());
return ssrb.build();
}

private MappingBinder createMappingBinder() {
return new MappingBinder(
MappingBinder.class.getClassLoader()::getResourceAsStream,
UnsupportedFeatureHandling.ERROR);
}

private List<Binding<JaxbHbmHibernateMapping>> getHbmMappings(MappingBinder mappingBinder) {
List<Binding<JaxbHbmHibernateMapping>> result = new ArrayList<>();
hbmXmlFiles.forEach((hbmXmlFile) -> {
final String fullPath = hbmXmlFile.getAbsolutePath();
LOGGER.info("Adding file: '" + fullPath + "' to the list to be transformed.");
HbmXmlOrigin origin = new HbmXmlOrigin( hbmXmlFile );
Binding<JaxbHbmHibernateMapping> binding = bindMapping( mappingBinder, origin );
result.add(binding);
});
return result;
}

private Binding<JaxbHbmHibernateMapping> bindMapping(
MappingBinder mappingBinder, HbmXmlOrigin origin) {
File hbmXmlFile = origin.getHbmXmlFile();
try ( final FileInputStream fileStream = new FileInputStream(hbmXmlFile) ) {
return mappingBinder.bind( fileStream, origin );
}
catch (IOException e) {
LOGGER.info( "Unable to open hbm.xml file `" + hbmXmlFile.getAbsolutePath() + "` for transformation");
return null;
}
}

private Marshaller createMarshaller(MappingBinder mappingBinder) {
try {
return mappingBinder.mappingJaxbContext().createMarshaller();
}
catch (JAXBException e) {
throw new RuntimeException("Unable to create JAXB Marshaller", e);
}
}

private void marshall(
Marshaller marshaller,
JaxbEntityMappingsImpl mappings,
File hbmXmlFile,
boolean formatResult) {
File mappingXmlFile = new File(
hbmXmlFile.getParentFile(),
hbmXmlFile.getName().replace(".hbm.xml", ".mapping.xml"));
LOGGER.info("Marshalling file: " + hbmXmlFile.getAbsolutePath() + " into " + mappingXmlFile.getAbsolutePath());
try {
marshaller.marshal( mappings, mappingXmlFile );
if (formatResult) {
XMLPrettyPrinter.prettyPrintFile(mappingXmlFile);
}
}
catch (JAXBException e) {
throw new RuntimeException(
"Unable to marshall mapping JAXB representation to file `" + mappingXmlFile.getAbsolutePath() + "`",
e);
}
catch (IOException e) {
throw new RuntimeException(
"Unable to format XML file `" + mappingXmlFile.getAbsolutePath() + "`",
e);
}
}

private void performTransformation(
List<Binding<JaxbHbmHibernateMapping>> hbmBindings,
MappingBinder mappingBinder,
ServiceRegistry serviceRegistry,
boolean formatResult) {
Marshaller marshaller = createMarshaller(mappingBinder);
MetadataSources metadataSources = new MetadataSources( serviceRegistry );
hbmBindings.forEach( metadataSources::addHbmXmlBinding );
List<Binding<JaxbEntityMappingsImpl>> transformedBindings = HbmXmlTransformer.transform(
hbmBindings,
(MetadataImplementor) metadataSources.buildMetadata(),
UnsupportedFeatureHandling.ERROR
);
for (int i = 0; i < hbmBindings.size(); i++) {
Binding<JaxbHbmHibernateMapping> hbmBinding = hbmBindings.get( i );
Binding<JaxbEntityMappingsImpl> transformedBinding = transformedBindings.get( i );
HbmXmlOrigin origin = (HbmXmlOrigin)hbmBinding.getOrigin();
File hbmXmlFile = origin.getHbmXmlFile();
marshall(marshaller, transformedBinding.getRoot(), hbmXmlFile, formatResult);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,214 @@
package org.hibernate.tool.internal.export.mapping;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import org.apache.commons.collections4.list.UnmodifiableList;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.internal.MappingBinder;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl;
import org.hibernate.boot.jaxb.spi.Binding;
import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.internal.util.DummyDialect;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static org.junit.jupiter.api.Assertions.*;

public class MappingExporterTest {

private MappingExporter mappingExporter;

@TempDir
private File tempDir;

@BeforeEach
public void beforeEach() {
mappingExporter = new MappingExporter();
}

@Test
public void testSetHbmFiles() throws NoSuchFieldException, IllegalAccessException {
Field hbmFilesField = MappingExporter.class.getDeclaredField("hbmXmlFiles");
List<File> origin = new ArrayList<>();
File fooFile = new File( tempDir, "foo.bar" );
origin.add( fooFile );
assertNotNull(hbmFilesField);
hbmFilesField.setAccessible(true);
assertNull(hbmFilesField.get(mappingExporter));
mappingExporter.setHbmFiles(origin);
List<?> destination = (List<?>) hbmFilesField.get(mappingExporter);
assertEquals(1, destination.size());
assertTrue(destination.contains(fooFile));
}

@Test
public void testCreateServiceRegistry()
throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Method createServiceRegistryMethod = MappingExporter.class.getDeclaredMethod("createServiceRegistry");
assertNotNull(createServiceRegistryMethod);
createServiceRegistryMethod.setAccessible(true);
Object object = createServiceRegistryMethod.invoke(mappingExporter);
assertNotNull(object);
assertInstanceOf(ServiceRegistry.class, object);
try (ServiceRegistry serviceRegistry = (ServiceRegistry) object) {
Dialect dialect = Objects.requireNonNull(serviceRegistry.getService(JdbcServices.class)).getDialect();
assertInstanceOf(DummyDialect.class, dialect);
}
}

@Test
public void testCreateMappingBinder()
throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Method createMappingBinderMethod = MappingExporter.class.getDeclaredMethod("createMappingBinder");
assertNotNull(createMappingBinderMethod);
createMappingBinderMethod.setAccessible(true);
Object object = createMappingBinderMethod.invoke(mappingExporter);
assertNotNull(object);
assertInstanceOf(MappingBinder.class, object);
MappingBinder mappingBinder = (MappingBinder) object;
assertNotNull(mappingBinder);
}

@Test
public void testBindMapping() throws Exception{
Method bindMappingMethod = MappingExporter.class.getDeclaredMethod(
"bindMapping",
MappingBinder.class,
HbmXmlOrigin.class);
assertNotNull(bindMappingMethod);
bindMappingMethod.setAccessible(true);
File file = new File(this.tempDir, "foo.bar");
Files.writeString(file.toPath(), "foobar");
final MappingBinder mappingBinder = new TestMappingBinder(file, "barfoo");
assertNotEquals("barfoo", Files.readString(file.toPath()));
Object object = bindMappingMethod.invoke(mappingExporter, mappingBinder, new HbmXmlOrigin(file));
assertInstanceOf(Binding.class, object);
Origin origin = ((Binding<?>)object).getOrigin();
assertInstanceOf(HbmXmlOrigin.class, origin);
assertSame(file, ((HbmXmlOrigin)origin).getHbmXmlFile());
assertEquals("barfoo", Files.readString(file.toPath()));
}

@Test
public void testGetHbmMappings() throws Exception {
Method getHbmMappingsMethod = MappingExporter.class.getDeclaredMethod(
"getHbmMappings",
MappingBinder.class);
assertNotNull(getHbmMappingsMethod);
getHbmMappingsMethod.setAccessible(true);
Field hbmFilesField = MappingExporter.class.getDeclaredField("hbmXmlFiles");
hbmFilesField.setAccessible(true);
List<File> hbmXmlFiles = new ArrayList<>();
File file = new File(this.tempDir, "foo.bar");
Files.writeString(file.toPath(), "foobar");
hbmXmlFiles.add(file);
hbmFilesField.set(mappingExporter, new UnmodifiableList<>(hbmXmlFiles));
final MappingBinder mappingBinder = new TestMappingBinder(file, "barfoo");
Object object = getHbmMappingsMethod.invoke(mappingExporter, mappingBinder);
assertInstanceOf(List.class, object);
assertEquals(1, ((List<?>)object).size());
object = ((List<?>) object).get(0);
assertInstanceOf(Binding.class, object);
Origin origin = ((Binding<?>)object).getOrigin();
assertInstanceOf(HbmXmlOrigin.class, origin);
assertSame(file, ((HbmXmlOrigin)origin).getHbmXmlFile());
assertEquals("barfoo", Files.readString(file.toPath()));
}

@Test
public void testCreateMarshaller() throws Exception {
Method createMarshallerMethod = MappingExporter.class.getDeclaredMethod(
"createMarshaller",
MappingBinder.class);
assertNotNull(createMarshallerMethod);
createMarshallerMethod.setAccessible(true);
final MappingBinder mappingBinder = new TestMappingBinder(new File("foo"), "foobar");
assertSame(
DUMMY_MARSHALLER,
createMarshallerMethod.invoke(mappingExporter, mappingBinder));
}

@Test
public void testMarshall() throws Exception {
Method marshallMethod = MappingExporter.class.getDeclaredMethod(
"marshall",
Marshaller.class,
JaxbEntityMappingsImpl.class,
File.class,
boolean.class);
assertNotNull(marshallMethod);
marshallMethod.setAccessible(true);
File hbmFile = new File(this.tempDir, "foo.hbm.xml");
File mappingFile = new File(this.tempDir, "foo.mapping.xml");
Files.writeString(mappingFile.toPath(), "<foo><bar>foobar</bar></foo>");
List<String> lines = Files.readAllLines(mappingFile.toPath());
assertEquals(1, lines.size());
marshallMethod.invoke(mappingExporter, DUMMY_MARSHALLER, null, hbmFile, true);
lines = Files.readAllLines(mappingFile.toPath());
assertEquals(4, lines.size());
}

@Disabled
@Test
public void testStart() {
MappingExporter exporter = new MappingExporter();
assertNotNull(exporter);
exporter.start();
}

private static final Marshaller DUMMY_MARSHALLER = (Marshaller) Proxy.newProxyInstance(
MappingExporterTest.class.getClassLoader(),
new Class<?>[]{Marshaller.class},
(proxy, method, args) -> {
if ("marshall".equals(method.getName())) {
Files.writeString(((File)args[1]).toPath(), "foobar");
}
return proxy;
}

);

private static class TestMappingBinder extends MappingBinder {
private final File f;
private final String s;
public TestMappingBinder(File f, String s) {
super(null, null, null);
this.f = f;
this.s = s;
}
@Override public <X extends JaxbBindableMappingDescriptor> org.hibernate.boot.jaxb.spi.Binding<X> bind(InputStream is, Origin o) {
try {
Files.writeString(f.toPath(), s);
}
catch (Exception ignore) {}
return new Binding<>(null, new HbmXmlOrigin(f));
}
@Override public JAXBContext mappingJaxbContext() {
return new JAXBContext() {
@Override
public Unmarshaller createUnmarshaller() {
return null;
}
@Override
public Marshaller createMarshaller() {
return DUMMY_MARSHALLER;
}
};
}
}

}