yGuard obfuscated jar will not connect to database with Hibernate

47 Views Asked by At

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.xml stored 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="&#x00a3;"/>
  <method class="org.hibernate.Metamodel" name="java.lang.String[] getImplementors(java.lang.String)" map="&#x00f3;"/>
  <field class="org.hibernate.UnknownProfileException" name="name" map="&#x00c0;"/>
  <method class="org.hibernate.UnknownProfileException" name="java.lang.String getName()" map="&#x00a4;"/>
  <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="&#x00c1;"/>
  <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>
1

There are 1 best solutions below

2
gbiz123 On

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:

<keep>
  <class methods="public" fields="public">
    <patternset>
       <include name="com.my.pattern.to.keep.*" />
    </patternset>
  </class>
</keep>

This is all explained in the class section of the yGuard documentation: https://yworks.github.io/yGuard/task_documentation.html#the-class-element