Objectbox-java: Class transformation conflicts, e.g. with Jacoco

Created on 11 Sep 2017  路  20Comments  路  Source: objectbox/objectbox-java

Issue Basics

  • ObjectBox version : 1.0.1
  • Reproducibility: always

Reproducing the bug

Make project is OK, but as soon as I want to run on AVD, build fails at :app:transformClassesWithObjectBoxAndroidTransformForDebug

(NB : also when above line is run as a single gradle command)

Code

Class is alfabetically first objectbox entity

package ... .entities;


import io.objectbox.annotation.Entity;
import io.objectbox.annotation.Id;
import io.objectbox.relation.ToOne;

/**
 * ObjectBox Data Class for text extract results
 * 
 */
@Entity
public class Extract {

  @Id
  long id;

  long termId;

  //relevant term
  public ToOne<Term> term;

  //relevant location id
  long locationId;

  String content;

   ...

  // Getters generated with Android studio

  public long getId() {
    return id;
  }

  public long getTermId() {
    return termId;
  }

  public long getLocationId() {
    return locationId;
  }

  public String getContent() {
    return content;
  }

   ...

  // Setters generated with Android studio

  public void setId(long id) {
    this.id = id;
  }

  public void setTermId(long termId) {
    this.termId = termId;
  }

  public void setLocationId(long locationId) {
    this.locationId = locationId;
  }

  public void setContent(String content) {
    this.content = content;
  }

   ...
}

Logs & stackstraces

Could not transform Cursor class ".. .entities.ExtractCursor" (Expected empty method body for ... .entities.ExtractCursor.attachEntity but was 10 long)

  • Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:transformClassesWithObjectBoxAndroidTransformForDebug'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:84)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:62)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
    at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:236)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:228)
    at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:61)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:228)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:215)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:77)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:58)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:32)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:113)
    at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
    at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
    at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
    at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
    at org.gradle.initialization.DefaultGradleLauncher$3.execute(DefaultGradleLauncher.java:196)
    at org.gradle.initialization.DefaultGradleLauncher$3.execute(DefaultGradleLauncher.java:193)
    at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:56)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:193)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:119)
    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:102)
    at org.gradle.launcher.exec.GradleBuildController.run(GradleBuildController.java:71)
    at org.gradle.tooling.internal.provider.runner.BuildModelActionRunner.run(BuildModelActionRunner.java:50)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.tooling.internal.provider.runner.RunAsBuildOperationBuildActionRunner$1.execute(RunAsBuildOperationBuildActionRunner.java:43)
    at org.gradle.tooling.internal.provider.runner.RunAsBuildOperationBuildActionRunner$1.execute(RunAsBuildOperationBuildActionRunner.java:40)
    at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
    at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:56)
    at org.gradle.tooling.internal.provider.runner.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:40)
    at org.gradle.tooling.internal.provider.runner.SubscribableBuildActionRunner.run(SubscribableBuildActionRunner.java:75)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:41)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:75)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:49)
    at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:44)
    at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:29)
    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:47)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
    at org.gradle.util.Swapper.swap(Swapper.java:38)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:72)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
    at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
Caused by: io.objectbox.gradle.transform.TransformException: Could not transform Cursor class "com.zyber.gomito.api.entities.ExtractCursor" (Expected empty method body for com.zyber.gomito.api.entities.ExtractCursor.attachEntity but was 10 long)
    at io.objectbox.gradle.transform.ClassTransformer.transformCursors(ClassTransformer.kt:297)
    at io.objectbox.gradle.transform.ClassTransformer.transformOrCopyClasses(ClassTransformer.kt:66)
    at io.objectbox.gradle.transform.ObjectBoxAndroidTransform.transform(ObjectBoxAndroidTransform.kt:89)
    at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:185)
    at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:181)
    at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:102)
    at com.android.build.gradle.internal.pipeline.TransformTask.transform(TransformTask.java:176)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:163)
    at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
    at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:123)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:95)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:76)
    ... 78 more
Caused by: io.objectbox.gradle.transform.TransformException: Expected empty method body for com.zyber.gomito.api.entities.ExtractCursor.attachEntity but was 10 long
    at io.objectbox.gradle.transform.ClassTransformer.transformCursor(ClassTransformer.kt:313)
    at io.objectbox.gradle.transform.ClassTransformer.transformCursors(ClassTransformer.kt:293)
    ... 90 more

Entities

Provide the code for entities related to the bug (shorten to relevant parts).

Misc

*Is there anything special about your app : uses retrolambda, proguard, dagger2 .
May transactions or multi-threading play a role: suppose not.
Did you find any workarounds to prevent the issue: not yet.

bug

Most helpful comment

Can confirm it's working.
Note that I have no direct JaCoCo test reports (yet) on ObjectBox, since JaCoCo also has quite some configuration to be done to run for AndroidTests and libraries.
But on some tests in my app, where I'm indirectly testing a library, I can now run a JaCoCo report, and the app build does not crash on it.

All 20 comments

Any idea if the classes get transformed twice? Is there a library module involved?

@greenrobot : no library module, but could database relations be the cause :
The entities schema is : Source |-> Term |->Extract ( |->: is a one to many relation).
Where can I find traces of duplicate transformation : don't see it in the stacktrace, nor in Android Studio log-files ?

Maybe you could do a ./gradlew build --debug and filter for objectbox/transform messages.
Will also think about how to detect/fix.

16:51:02.616 [QUIET] [system.out] Checking to transform entity "... .entities.Extract" (has relations: true)
16:51:02.660 [QUIET] [system.out] Writing transformed entity "... .entities.Extract"
16:51:02.663 [QUIET] [system.out] Checking to transform entity "... .entities.Location" (has relations: true)
16:51:02.664 [QUIET] [system.out] Writing transformed entity "... .entities.Location"
16:51:02.666 [QUIET] [system.out] Checking to transform entity "... .entities.Source" (has relations: true)
16:51:02.667 [QUIET] [system.out] Writing transformed entity "... .entities.Source"
16:51:02.670 [QUIET] [system.out] Checking to transform entity "... .entities.Term" (has relations: true)
16:51:02.672 [QUIET] [system.out] Writing transformed entity "... .entities.Term"
16:51:02.673 [QUIET] [system.out] Checking to transform entity "... .entities.User" (has relations: false)
16:51:02.681 [DEBUG] [org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter] Removed task artifact state for {} from c
ontext.
16:51:02.681 [DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Finished executing task ':app:transformClassesWi
thObjectBoxAndroidTransformForDebug'
16:51:02.681 [LIFECYCLE] [class org.gradle.internal.buildevents.TaskExecutionLogger] :app:transformClassesWithObjectBoxAndroidTransformForDeb
ug FAILED

Does this shine a light ?

Hmm, not really...

Could you verify that it is the relation causing this? E.g. commenting it out?

PS.: Another info would help: could you decompile the com.zyber.gomito.api.entities.ExtractCursor class (search in the build folder for it) and post the contents of the attach method. Just to verify it was actually ObjectBox in the first place...

Verified it is the relation causing this, I commented all the one-to many's out, up to Source|-> Term.
After re-enabling one-to-many for this pair, I decompiled SourceCursor (error is the same as what it was for ExtractCursor)
Below is attachEntity method (but it's empty, followed by a static {} )+ reference in put method (no other references to "attachentity" in decompiled class):

```
public final long put(Source entity) {
String uri = entity.uri;
int __id1 = uri != null?__ID_uri:0;
String title = entity.title;
int __id2 = title != null?__ID_title:0;
collect400000(this.cursor, 0L, 1, __id1, uri, __id2, title);
String body = entity.body;
int __id7 = body != null?__ID_body:0;
long __assignedId = collect313311(this.cursor, entity.id, 2, __id7, body, 0, (String)null, 0, (byte[])null, __ID_analyzed, entity.analyzed?1L:0L, 0, 0L, 0, 0, 0, 0, 0, 0, 0, 0.0F, 0, 0.0D);
entity.id = __assignedId;
this.attachEntity(entity);
ToMany terms = entity.terms;
if(terms instanceof ToMany) {
ToMany toMany = (ToMany)terms;
if(toMany.internalCheckApplyToDbRequired()) {
Cursor targetCursor = this.getRelationTargetCursor(Term.class);

    try {
      toMany.internalApplyToDb(this, targetCursor);
    } finally {
      targetCursor.close();
    }
  }
}

return __assignedId;

}

private void attachEntity(Source entity) {
}
static {
ID_GETTER = Source_.__ID_GETTER;
__ID_uri = Source_.uri.id;
__ID_title = Source_.title.id;
__ID_analyzed = Source_.analyzed.id;
__ID_body = Source_.body.id;
}

````

@greenrobot : apparently this is a conflict with Jacoco : having testCoverageEnabled = true triggered this in my gradle file (I also had a jacoco reporting plugin, but even after this was disabled, error was still there, because jacoco transform folder was also).
Because jacoco also transforms and injects variables in attachEntity method in the cursor class
(but in it's own transform folder, not the objectBox folder, that's what I misunderstood from your question). So case closed as far as I am concerned, now of to look for a test coverage alternative ?!

I'd like to keep that issue open - maybe we can do something clever n our side.

@GerritDeMeulder is there a way to tell Jacoco to exclude classes/packages/folders?

@greenrobot :
I'm not a Jacoco -, nor Gradle expert, so I'm using a file based on the Jacoco gradle file from storio/gradle/jacoco-android.gradle .
Modifying the file, by adding a line for objectbox like this seems to work partially :

...
classDirectories = fileTree(
                        dir: "${project.buildDir}/intermediates/classes/${sourcePath}",
                        excludes: ['**/R.class',
                                   '**/R$*.class',
                                   '**/*$ViewInjector*.*',
                                   '**/*$ViewBinder*.*',
                                   '**/BuildConfig.*',
                                   '**/Manifest*.*',
                                   '**/*Module.*', // Modules for Dagger.
                                   '**/*Dagger*.*', // Dagger auto-generated code.
                                   '**/*MembersInjector*.*', // Dagger auto-generated code.
                                   '**/*_Provide*Factory*.*',// Dagger auto-generated code.
                                   '**/*ObjectBox*.*', // ObjectBox auto-generated code.
                        ]
                )

What doesn't work yet is setting back testCoverageEnabled = true
this still causes the original problem.
(testCoverageEnabled = true runs jacoco for instrumentation tests, from what I understand , while the jacoco plugin is for unit tests).

Note that I'm trying to tie in objectBox in an existing project
and many tests that are run, fail due to following (unrelated) Robolectric error :
(Seems it can't get to boxStore = MyObjectBox.builder().androidContext(GoMitoApp.this).build();:

Jacoco Test failure description:

java.lang.NoClassDefFoundError: Could not initialize class io.objectbox.internal.NativeLibraryLoader
    at io.objectbox.BoxStore.<init>(BoxStore.java:162)
    at io.objectbox.BoxStoreBuilder.build(BoxStoreBuilder.java:243)
    at com.zyber.gomito.GoMitoApp.onCreate(GoMitoApp.java:62)
    at org.robolectric.android.internal.ParallelUniverse.setUpApplicationState(ParallelUniverse.java:137)

Error seen when running a single test in this test file:

No such manifest file: buildintermediatesbundlesdebugAndroidManifest.xml
Not available in classpath: /native/objectbox-windows-x64.dll
File not available: C:...GoMitoobjectbox-windows-x64.dll
java.lang.UnsatisfiedLinkError: no objectbox-windows-x64 in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at io.objectbox.internal.NativeLibraryLoader.(NativeLibraryLoader.java:44)
at io.objectbox.BoxStore.(BoxStore.java:162)
at io.objectbox.BoxStoreBuilder.build(BoxStoreBuilder.java:243)
at com.zyber.gomito.GoMitoApp.onCreate(GoMitoApp.java:62)
...

edit : while checking stackoverflow , I came across a similar problem for GreenDAO : Exclude package from jacoco code coverage for Android

I would try to experiment with the Jacoco config a bit, e.g. exclude something like this:

 '**/*Cursor*.class',

"no objectbox-windows-x64 in java.library.path" is tracked by #170 - we are on it currently.

Note to self: maybe we could check existing code if "__boxStore" is assigned, and only then throw the error.

I get same error...
How to fix?

Hi @tatsuyuki25, it's not an easy problem to solve : the excludes I mentioned above only make jacoco exclude these classes from its reporting. But as far as I was able to test, it still changes the intermediate build classes. Maybe a possible way to patch this is using intelij IDEA, import the project and run the tests with built in Idea coverage, see here: https://www.jetbrains.com/help/idea/running-with-coverage.html.
But I haven't had the time to test this yet.

Same error here.
Boring :(

Any updates on this?

This should be fixed once the upcoming changes as mentioned in https://github.com/objectbox/objectbox-java/issues/555#issuecomment-419902976 are released.
-ut

Changes approved, this will be fixed with the next update.

Please re-open if the next update does not fix this issue.
-ut

We just released version 2.2.0 with a fix. Please let us know if it is working for you. Thanks!

Can confirm it's working.
Note that I have no direct JaCoCo test reports (yet) on ObjectBox, since JaCoCo also has quite some configuration to be done to run for AndroidTests and libraries.
But on some tests in my app, where I'm indirectly testing a library, I can now run a JaCoCo report, and the app build does not crash on it.

Was this page helpful?
0 / 5 - 0 ratings