Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better error reporting for custom setters/getters #952

Closed
kboyarshinov opened this issue Mar 12, 2015 · 19 comments
Closed

Better error reporting for custom setters/getters #952

kboyarshinov opened this issue Mar 12, 2015 · 19 comments

Comments

@kboyarshinov
Copy link

I have an app with multidex build enabled. After updating Realm to 0.80.0 it crashes at first request to Realm:

 io.realm.exceptions.RealmException: Could not find io.realm.RealmJsonImpl
        at io.realm.Realm.getRealmJson(Realm.java:268)
        at io.realm.Realm.<init>(Realm.java:164)
        at io.realm.Realm.createAndValidate(Realm.java:522)
        at io.realm.Realm.create(Realm.java:488)
        at io.realm.Realm.getInstance(Realm.java:404)
        at io.realm.Realm.getInstance(Realm.java:366)
 ...
 Caused by: java.lang.ClassNotFoundException: io.realm.RealmJsonImpl
        at java.lang.Class.classForName(Native Method)

It works fine on 0.79.0. And 0.80.0 works fine in app without multidex. I guess this is related. Clean build does not resolve a problem.

Similar issues: #860, #947

OS: OS X 10.10.2
Android build tools version: 21.1.2
Android gradle plugin version: 1.1.0
JDK version: 1.7

Inspected build logs. 0.79.0 version of Realm annotation processor processes all of my RealmObject classes (I have 14). But 0.80.0 processed only 3 of them - only 3 lines of Note: Processing class ... appears in build output.

@cmelchior
Copy link
Contributor

Does this also happen if you try to compile from the command line with: ./gradlew clean assemble?

@kboyarshinov
Copy link
Author

@cmelchior yes

@cmelchior
Copy link
Contributor

That is really odd. I could imagine there could be problems with if the dexer puts the RealmJsonImpl in the secondary dex file (see under multidex limitations here: https://developer.android.com/tools/building/multidex.html)

But the annotation processor runs as part of the Java compiler so if it doesn't find all your model classes, something else is wrong.

  • Is all your code in one project or have you split things between a library project and app project?
  • Are you in a position to share the project so we can see a reproducible case?

@kboyarshinov
Copy link
Author

Yes, all Realm related code is in app project.

I cannot share this exact project but I started working on reproducible example. Will post it here when it's done or if it becomes clear that something else is wrong.

@cmelchior
Copy link
Contributor

Thank you, that will be very helpfull.

@iflatness
Copy link

In case it helps, my app uses multidex but does not use RealmJson and is building successfully under 0.80.0.

@kboyarshinov
Copy link
Author

I figured out what the problem was. Multidex is not related to it.

Several of my realm classes implement interface like that:

public interface Model {
    int getX();
}

public class RealmModel extends RealmObject implements Model {
   // fields, getters and setters

   int getX() {
       return 1;
   }
}

Implementations have nothing to do with internal fields that are actually stored with that class data in realm or any getters and setters. Previous versions of Realm had less stricter rules for defining objects than 0.80 has. Getters not associated with to any field were allowed with warning message during build process ("Note Getter getX is not associated to any field"). In 0.80 as I see from ClassMetaData and RealmProcessor here if any class fails meta data check further annotation processing stops. Although it fires a warning log.

I guess it would be better to fire error to Messager here and here. It will give clear understanding why annotation processing aborts - something wrong with declared realm objects. Also it should be covered in documentation and changelogs that custom getters and setters with no associated fields are not allowed since 0.80 and will cause a build error.

I can make a PR if my suggestion fits right.

@cmelchior
Copy link
Contributor

Yes, we did restructure some things in the annotation processor because the code was getting unmanageable. That these setters/getters was allowed before was mostly an oversigt on our part and shouldn't have been possible until we could add better support overall for them.

But a PR with better error reporting would be most welcome.

@cmelchior cmelchior changed the title Failed to build app with multidex after update to 0.80.0 Better error reporting for custom setters/getters Mar 12, 2015
@kboyarshinov
Copy link
Author

Great. I'll send PR.

@cmelchior
Copy link
Contributor

Fix has been merged to master. Thanks for the PR.

@emreaktrk
Copy link

I am able to reproduce the bug.

https://github.com/emreaktrk/EdgeContact/tree/bug/realm

@marcel-eggum
Copy link

Oh. This is a terrible deal-breaker for me.
I previously used v0.79.1 and leveraged an interface called "Model" in order to implement generic save-or-update functions for all my models.

public interface Model
{
    public static final String ID = "remoteId";

    public String getRemoteId();

    public void setRemoteId(String id);
}

Is there anything that I can do in order to force Realm v.0.80.0 to accept my models?

@cmelchior
Copy link
Contributor

Hi @marcel-eggum
As long you as the model classes has a String field called "remoteId" that interface should work? We don't actively disallow interfaces, however if the methods required by the interface doesn't match the fields inside the model class you will get an build error.

@marcel-eggum
Copy link

Well, an example of a given model kan be seen here:

public class Message extends RealmObject implements Model, Serializable
{
    @PrimaryKey
    private String remoteId;

    private String title;
    private String caption;
    private String kind;
    private RealmList<Media> media;
    private RealmList<Section> texts;

    @Override
    public String getRemoteId()
    {
        return remoteId;
    }

    @Override
    public void setRemoteId(String id)
    {
        this.remoteId = id;
    }

   etc..

I find it strangly odd that this solution is no longer working as I retrieve the exact same Exceptions as @kboyarshinov notes in his original post.

The use-case for such a solution is strongly cupeled to our generic-save function, where we create-or-update our entities when we synchronize with the backend - or delete unwanted entities if they are no longer present in the response that we retrieve:

/**
     * Persists a given collection on items that
     * correspond to this repositories type;
     *
     * @param items  The list of models to save
     * @param clear  Clear the repository
     * @param filter Remove all, old items that do not match any of the @PrimaryKey's in items
     * @param <T>
     */
    public <T extends RealmObject & Model> void save(final List<T> items, final boolean clear, final boolean filter)
    {

        realm.executeTransaction(new Realm.Transaction()
        {
            @Override
            public void execute(Realm realm)
            {
                if (clear)
                {
                    realm.clear(type);
                }

                List<T> saved = realm.copyToRealmOrUpdate(items);

                if (filter)
                {

                    RealmQuery<T> query = realm.where(type);

                    for (T entity : saved)
                    {
                        query.notEqualTo(Model.ID, entity.getRemoteId());
                    }

                    List<T> expired = new ArrayList<>(query.findAll());

                    for (T ex : expired)
                    {
                        ex.removeFromRealm();
                    }
                }
            }
        });
    }

The primary use of such an interface - is to ensure for the save-function that a given model has a field called "remoteId". This strategy allows us to ensure that all developers use the appropriate pattern when they add new models in the future.

Oh. And its not a build error. Its a runtime-error.

@cmelchior
Copy link
Contributor

The error message usually happens because the Annotation processor didn't complete, and you code you posted seems to look fine.

Can you post the output of: ./gradlew clean assemble?

@marcel-eggum
Copy link

Sure.

Pling$ ./gradlew clean assemble

:app:clean

:app:preBuild

:app:clean

:app:preBuild

:app:compileDevelopDebugNdk

:app:preDevelopDebugBuild

:app:checkDevelopDebugManifest

:app:preDevelopReleaseBuild

:app:preProductionDebugBuild

:app:preProductionReleaseBuild

:app:prepareComAndroidSupportAppcompatV72103Library

:app:prepareComAndroidSupportSupportV42103Library

:app:prepareDevelopDebugDependencies

:app:compileDevelopDebugAidl

:app:compileDevelopDebugRenderscript

:app:generateDevelopDebugBuildConfig

:app:generateDevelopDebugAssets UP-TO-DATE

:app:mergeDevelopDebugAssets

:app:generateDevelopDebugResValues UP-TO-DATE

:app:generateDevelopDebugResources

:app:mergeDevelopDebugResources

:app:processDevelopDebugManifest

:app:processDevelopDebugResources

:app:generateDevelopDebugSources

:app:compileDevelopDebugJava

Note: Processing class Conference

Note: Processing class Media

Note: Getter getMediaType is not associated to any field

Note: Recompile with -Xlint:deprecation for details.

Note: Some input files use unchecked or unsafe operations.

Note: Recompile with -Xlint:unchecked for details.

:app:preDexDevelopDebug

:app:dexDevelopDebug

:app:processDevelopDebugJavaRes UP-TO-DATE

:app:validateDebugSigning

:app:packageDevelopDebug

:app:zipalignDevelopDebug

:app:assembleDevelopDebug

:app:assembleRelease

:app:assemble


BUILD SUCCESSFUL



Total time: 2 mins 0.357 secs

"GetMediaType" is a String to enum converter annotated with the @ignore property:

@Ignore
    public MediaType getMediaType()
    {
        return MediaType.valueOf(getKind().toUpperCase());
    }

@marcel-eggum
Copy link

Actually. The error occurs in relation to Realm Initialization

Caused by: io.realm.exceptions.RealmException: Could not find io.realm.RealmJsonImpl
            at io.realm.Realm.getRealmJson(Realm.java:268)
            at io.realm.Realm.<init>(Realm.java:164)
            at io.realm.Realm.createAndValidate(Realm.java:522)
            at io.realm.Realm.create(Realm.java:486)
            at io.realm.Realm.getInstance(Realm.java:404)
            at io.realm.Realm.getInstance(Realm.java:366)
            at io.realm.Realm.getInstance(Realm.java:347)
            at hyper.rema.riks.io.repository.Repository.<init>(Repository.java:28)
            at hyper.rema.riks.io.repository.MessageRepository.<init>(MessageRepository.java:15)

The implementation of Repository.java looks like this:

public abstract class Repository<T extends RealmObject>
{
    protected Realm realm;
    protected Class type;

    public Repository(Class type)
    {
        this.type = type;
        this.realm = Realm.getInstance(App.getContext());
    }

etc...

The intention of Repository.java is to withhold a set of common methods like, save() and list() as they are so commonly used.

@cmelchior
Copy link
Contributor

Hi @marcel-eggum
We don't support the @Ignore annotation on methods. It is a bug that you only get a note in the build log instead of an error (which would have aborted your build). This PR fixes that.

You can try to use the latest SNAPSHOT from master to see this behavior:

    repositories {
        jcenter()
        maven {
            url 'http://oss.jfrog.org/artifactory/oss-snapshot-local'
        }
    }

    compile 'io.realm:realm-android:0.80.1-SNAPSHOT'

Model classes do support static methods though, so until we can implement custom methods you can do something like this instead:

public static MediaType getMediaType(Media media)
    {
        return MediaType.valueOf(media.getKind().toUpperCase());
    }

@marcel-eggum
Copy link

Ah!
Confirmed. The error was that I annotated functions with @ignore.
Refactoring them to become static, solved all issues.

Thank you @cmelchior.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 18, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants