I am having trouble getting hibernate to play nice with yGuard in my single-jar application that was packaged with the maven-asssembly plugin. The application is unable to initialize the database connection when it tries to create the entity manager.
Running a non-obfuscated jar created using the maven assembly plugin runs just fine. Also, running a multi-file jar with an external lib/ directory works fine. I am running into issues with persistence when I try to create a single jar that is also obfuscated.
Could someone take a look at my yGuard and hibernate configurations to help me figure out why the obfuscated jar will not properly connect to the database?
Here's what I have tried:
- Preventing yGuard from obfuscating my persistence layer with the
<keep>tag - Switching from
persistence.xmlstored in resources to programmatic hibernate configuration
The application launches, but fails to create the database connection when my Database utility class is initialized:
public class Database {
private static SessionFactory factory;
static {
Configuration configuration = new Configuration();
configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
configuration.setProperty("hibernate.hbm2ddl.auto", "update");
configuration.setProperty("javax.persistence.jdbc.driver", "org.h2.driver");
configuration.setProperty("javax.persistence.jdbc.url",
"jdbc:h2:file:./hyperaccsdb");
configuration.addAnnotatedClass(Account.class);
configuration.addAnnotatedClass(AccountCookie.class);
configuration.addAnnotatedClass(EmailAccount.class);
configuration.addAnnotatedClass(EmailInbox.class);
configuration.addAnnotatedClass(EmailPlatform.class);
configuration.addAnnotatedClass(Avatar.class);
configuration.addAnnotatedClass(BrowserConfig.class);
factory = configuration.buildSessionFactory();
}
public static EntityManager getEntityManager() {
return factory.createEntityManager();
}
}
Here is the stacktrace I get when trying to initialize the database connection:
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: Could not initialize class com.toughdata.autogrowth.persistence.Database 13:36:54 [41/1289]
at com.toughdata.autogrowth.persistence.dao.EmailPlatformDaoImpl.A(Unknown Source)
at com.toughdata.autogrowth.B.F.A(Unknown Source)
at com.toughdata.autogrowth.B.R.A(Unknown Source)
at com.toughdata.autogrowth.C.M.A(Unknown Source)
at com.toughdata.autogrowth.C.I.D(Unknown Source)
at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2313)
at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
at java.desktop/java.awt.Component.processMouseEvent(Component.java:6626)
at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3389)
at java.desktop/java.awt.Component.processEvent(Component.java:6391)
at java.desktop/java.awt.Container.processEvent(Container.java:2266)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4575)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.ExceptionInInitializerError [in thread "SwingWorker-pool-2-thread-1"]
at net.bytebuddy.implementation.bind.MethodDelegationBinder$AmbiguityResolver.<clinit>(Unknown Source)
at net.bytebuddy.implementation.MethodDelegation.L(Unknown Source)
at net.bytebuddy.implementation.MethodDelegation.B(Unknown Source)
at org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState$ProxyDefinitionHelpers$1.A(Unknown Source)
at org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState$ProxyDefinitionHelpers$1.run(Unknown Source)
at org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState$ProxyDefinitionHelpers.<init>(Unknown Source)
at org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState.<clinit>(Unknown Source)
at org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl.<init>(Unknown Source)
at org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl.<init>(Unknown Source)
at org.hibernate.bytecode.internal.BytecodeProviderInitiator.A(Unknown Source)
at org.hibernate.bytecode.internal.BytecodeProviderInitiator.W(Unknown Source)
at org.hibernate.bytecode.internal.BytecodeProviderInitiator.A(Unknown Source)
at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.A(Unknown Source)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.F(Unknown Source)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.G(Unknown Source)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.A(Unknown Source)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.<init>(Unknown Source)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.<init>(Unknown Source)
at org.hibernate.boot.internal.DefaultSessionFactoryBuilderService.A(Unknown Source)
at org.hibernate.boot.internal.MetadataImpl.ʦ(Unknown Source)
at org.hibernate.cfg.Configuration.B(Unknown Source)
at org.hibernate.cfg.Configuration.C(Unknown Source)
at com.toughdata.autogrowth.persistence.Database.<clinit>(Unknown Source)
at com.toughdata.autogrowth.persistence.dao.EmailPlatformDaoImpl.B(Unknown Source)
at com.toughdata.autogrowth.B.O.A(Unknown Source)
at com.toughdata.autogrowth.B.R.A(Unknown Source)
at com.toughdata.autogrowth.C.M.A(Unknown Source)
at com.toughdata.autogrowth.C.I$1.A(Unknown Source)
at com.toughdata.autogrowth.C.I$1.doInBackground(Unknown Source)
at java.desktop/javax.swing.SwingWorker$1.call(SwingWorker.java:304)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.desktop/javax.swing.SwingWorker.run(SwingWorker.java:343)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
Here is my yGuard ant task:
<yguard>
<inoutpair in="${project.jar.unobf}" out="${project.jar}" />
<rename
logfile="${project.build.directory}/${project.build.finalName}_renamelog.xml">
<adjust replacePath="false">
<include name="/resources/*" />
</adjust>
<adjust replaceContent="true" replaceContentSeparator=".">
<include name="**/*.xml" />
<include name="**/*.dll" />
<include name="**/*.so" />
</adjust>
<keep>
<method name="void main(java.lang.String[])"
class="${mainclass}" />
<class>
<patternset>
<include name="**.*" />
<exclude name="com.toughdata.autogrowth.gui.*" />
<exclude
name="com.toughdata.autogrowth.automation.**.*" />
<exclude
name="com.toughdata.autogrowth.commands.**.*" />
<exclude name="com.toughdata.autogrowth.auth.*" />
</patternset>
</class>
<field name="*">
<patternset>
<include
name="com.toughdata.autogrowth.persistence.entities.*" />
</patternset>
</field>
<field
class="org.apache.logging.log4j.core.config.AppenderControlArraySet"
name="appenderArray" />
<field
class="com.toughdata.autogrowth.persistence.Database"
name="factory" />
</keep>
</rename>
</yguard>
EDIT:
Looking into yGuard's rename logs I can see that Hibernate classes are being renamed for some reason. I will add a keep rule to these and see what happens. I thought my pattern **.* should have excluded them, but maybe I need to be more specific. Here is a snippet of the rename log:
<method class="org.hibernate.StaleObjectStateException" name="java.lang.String getEntityName()" map="£"/>
<method class="org.hibernate.Metamodel" name="java.lang.String[] getImplementors(java.lang.String)" map="ó"/>
<field class="org.hibernate.UnknownProfileException" name="name" map="À"/>
<method class="org.hibernate.UnknownProfileException" name="java.lang.String getName()" map="¤"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy$AttributeInformation" name="org.hibernate.type.Type getType()" map="A"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy$AttributeInformation" name="java.lang.Object getLoadedValue()" map="B"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy$AttributeInformation" name="java.lang.String getName()" map="C"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy$AttributeInformation" name="int getAttributeIndex()" map="D"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy$AttributeInformation" name="org.hibernate.persister.entity.EntityPersister getContainingPersister()" map="E"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy$AttributeInformation" name="java.lang.Object getCurrentValue()" map="F"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy$AttributeChecker" name="boolean isDirty(org.hibernate.CustomEntityDirtinessStrategy$AttributeInformation)" map="A"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy$DirtyCheckContext" name="void doDirtyChecking(org.hibernate.CustomEntityDirtinessStrategy$AttributeChecker)" map="A"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy" name="boolean canDirtyCheck(java.lang.Object, org.hibernate.persister.entity.EntityPersister, org.hibernate.Session)" map="A"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy" name="boolean isDirty(java.lang.Object, org.hibernate.persister.entity.EntityPersister, org.hibernate.Session)" map="B"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy" name="void resetDirty(java.lang.Object, org.hibernate.persister.entity.EntityPersister, org.hibernate.Session)" map="C"/>
<method class="org.hibernate.CustomEntityDirtinessStrategy" name="void findDirty(java.lang.Object, org.hibernate.persister.entity.EntityPersister, org.hibernate.Session, org.hibernate.CustomEntityDirtinessStrategy$DirtyCheckContext)" map="A"/>
<class name="org.hibernate.CacheMode$1" map="1"/>
<field class="org.hibernate.CacheMode$1" name="$SwitchMap$jakarta$persistence$CacheStoreMode" map="A"/>
<field class="org.hibernate.CacheMode$1" name="$SwitchMap$jakarta$persistence$CacheRetrieveMode" map="B"/>
<field class="org.hibernate.CacheMode" name="REFRESH" map="A"/>
<field class="org.hibernate.CacheMode" name="GET" map="B"/>
<field class="org.hibernate.CacheMode" name="retrieveMode" map="C"/>
<field class="org.hibernate.CacheMode" name="$VALUES" map="D"/>
<field class="org.hibernate.CacheMode" name="PUT" map="E"/>
<field class="org.hibernate.CacheMode" name="NORMAL" map="F"/>
<field class="org.hibernate.CacheMode" name="storeMode" map="G"/>
<field class="org.hibernate.CacheMode" name="IGNORE" map="H"/>
<method class="org.hibernate.CacheMode" name="boolean isGetEnabled()" map="A"/>
<method class="org.hibernate.CacheMode" name="org.hibernate.CacheMode interpretExternalSetting(java.lang.String)" map="A"/>
<method class="org.hibernate.CacheMode" name="jakarta.persistence.CacheStoreMode getJpaStoreMode()" map="B"/>
<method class="org.hibernate.CacheMode" name="org.hibernate.CacheMode[] values()" map="values"/>
<method class="org.hibernate.CacheMode" name="boolean isPutEnabled()" map="C"/>
<method class="org.hibernate.CacheMode" name="org.hibernate.CacheMode fromJpaModes(jakarta.persistence.CacheRetrieveMode, jakarta.persistence.CacheStoreMode)" map="A"/>
<method class="org.hibernate.CacheMode" name="org.hibernate.CacheMode valueOf(java.lang.String)" map="valueOf"/>
<method class="org.hibernate.CacheMode" name="jakarta.persistence.CacheRetrieveMode getJpaRetrieveMode()" map="D"/>
<field class="org.hibernate.TransientPropertyValueException" name="propertyName" map="i"/>
<field class="org.hibernate.TransientPropertyValueException" name="transientEntityName" map="j"/>
<field class="org.hibernate.TransientPropertyValueException" name="propertyOwnerEntityName" map="k"/>
<method class="org.hibernate.TransientPropertyValueException" name="java.lang.String getMessage()" map="getMessage"/>
<method class="org.hibernate.TransientPropertyValueException" name="java.lang.String getPropertyOwnerEntityName()" map="a"/>
<method class="org.hibernate.TransientPropertyValueException" name="java.lang.String getTransientEntityName()" map="b"/>
<method class="org.hibernate.TransientPropertyValueException" name="java.lang.String getPropertyName()" map="c"/>
<field class="org.hibernate.LazyInitializationException" name="LOG" map="Á"/>
<method class="org.hibernate.SessionFactoryObserver" name="void sessionFactoryCreated(org.hibernate.SessionFactory)" map="A"/>
<method class="org.hibernate.SessionFactoryObserver" name="void sessionFactoryClosing(org.hibernate.SessionFactory)" map="B"/>
<method class="org.hibernate.SessionFactoryObserver" name="void sessionFactoryClosed(org.hibernate.SessionFactory)" map="C"/>
</map>
It looks like my issue came from a lack of understanding about yGuard's keep process. After looking at the keep logs, I noticed that all class names in Hibernate were kept, but all the methods and fields were changing. My initial assumption was that because I kept the classes, the fields and methods would also be kept. This is not the case. When telling yGuard to keep classes from external libraries, you must explicitly specify the visibility of the methods and fields to keep.
Here's how the keep rule needs to be defined:
This is all explained in the
classsection of the yGuard documentation: https://yworks.github.io/yGuard/task_documentation.html#the-class-element