<!-- 
RSS generated by JIRA (9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66) at Thu Feb 08 08:57:39 UTC 2024

It is possible to restrict the fields that are returned in this document by specifying the 'field' parameter in your request.
For example, to request only the issue key and summary append 'field=key&field=summary' to the URL of your request.
-->
<rss version="0.92" >
<channel>
    <title>MongoDB Jira</title>
    <link>https://jira.mongodb.org</link>
    <description>This file is an XML representation of an issue</description>
    <language>en-us</language>    <build-info>
        <version>9.7.1</version>
        <build-number>970001</build-number>
        <build-date>13-04-2023</build-date>
    </build-info>


<item>
            <title>[JAVA-2612] Improve POJO Codec - support sub-classes with the bson creator</title>
                <link>https://jira.mongodb.org/browse/JAVA-2612</link>
                <project id="10006" key="JAVA">Java Driver</project>
                    <description>&lt;p&gt;PR: &lt;a href=&quot;https://github.com/mongodb/mongo-java-driver/pull/417&quot; class=&quot;external-link&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener&quot;&gt;https://github.com/mongodb/mongo-java-driver/pull/417&lt;/a&gt;&lt;/p&gt;</description>
                <environment></environment>
        <key id="432185">JAVA-2612</key>
            <summary>Improve POJO Codec - support sub-classes with the bson creator</summary>
                <type id="4" iconUrl="https://jira.mongodb.org/secure/viewavatar?size=xsmall&amp;avatarId=14710&amp;avatarType=issuetype">Improvement</type>
                                            <priority id="3" iconUrl="https://jira.mongodb.org/images/icons/priorities/major.svg">Major - P3</priority>
                        <status id="6" iconUrl="https://jira.mongodb.org/images/icons/statuses/closed.png" description="The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.">Closed</status>
                    <statusCategory id="3" key="done" colorName="success"/>
                                    <resolution id="13201">Fixed</resolution>
                                        <assignee username="ross@mongodb.com">Ross Lawley</assignee>
                                    <reporter username="ross@mongodb.com">Ross Lawley</reporter>
                        <labels>
                    </labels>
                <created>Mon, 25 Sep 2017 09:27:58 +0000</created>
                <updated>Sun, 29 Oct 2023 02:32:35 +0000</updated>
                            <resolved>Mon, 4 Dec 2017 09:47:40 +0000</resolved>
                                                    <fixVersion>3.6.0</fixVersion>
                                    <component>POJO</component>
                                        <votes>0</votes>
                                    <watches>1</watches>
                                                                                                                <comments>
                            <comment id="1742110" author="xgen-internal-githook" created="Mon, 4 Dec 2017 20:41:46 +0000"  >&lt;p&gt;Author:&lt;/p&gt;
{&apos;username&apos;: &apos;jflorencio&apos;, &apos;email&apos;: &apos;joe@deal.com&apos;, &apos;name&apos;: &apos;Joseph Florencio&apos;}
&lt;p&gt;Message: PojoCodec improvents for sub-classes&lt;/p&gt;

&lt;p&gt;Pojo Codec fixes with sub-classes and bson creator&lt;/p&gt;

&lt;p&gt;This commit tries to fix a few issues:&lt;/p&gt;

&lt;p&gt;1. It was not possible to overload a getter in a sub-class and make the&lt;br/&gt;
type more specific (covariant overload). For example, attempting to&lt;br/&gt;
serialize Bar would fail:&lt;/p&gt;

&lt;p&gt;```&lt;br/&gt;
  // Assume there are setters here as well...&lt;br/&gt;
  class Foo {&lt;br/&gt;
    public Number getNumber() &lt;/p&gt;
{ /*...*/ }&lt;br/&gt;
  }&lt;br/&gt;
&lt;br/&gt;
  class Bar extends Foo {&lt;br/&gt;
    @Override&lt;br/&gt;
    public Long getNumber() { /*...*/ }
&lt;p&gt;  }&lt;br/&gt;
```&lt;/p&gt;

&lt;p&gt;This was due to bridge methods not getting picked up, so even though&lt;br/&gt;
Method#getDeclaredMethods is documented to return methods only from the&lt;br/&gt;
super class, the Java compiler inserts a bridge method in the sub-class&lt;br/&gt;
(Bar in this case) for these covariant overrides for legacy interop.&lt;/p&gt;

&lt;p&gt;2.a. It was not possible to use a more specific type in a getter, but a&lt;br/&gt;
more general type in the setter/constructor/static factory method. For example:&lt;/p&gt;

&lt;p&gt;```&lt;br/&gt;
class Foo {&lt;br/&gt;
  public ImmutableList&amp;lt;String&amp;gt; getNames() &lt;/p&gt;
{ /*...*/ }&lt;br/&gt;
  // Mutable class&lt;br/&gt;
  public void setNames(List&amp;lt;String&amp;gt; names) { /*...*/ }
&lt;p&gt;  // or immutable class (both didn&apos;t work)&lt;br/&gt;
  @BsonCreator&lt;br/&gt;
  public Foo(@BsonProperty(&quot;names&quot;) List&amp;lt;String&amp;gt; names) &lt;/p&gt;
{ /*...*/ }
&lt;p&gt;}&lt;br/&gt;
```&lt;/p&gt;

&lt;p&gt;The first issue was that strict type equality was enforced. This is a&lt;br/&gt;
pretty common pattern that should be allowed though - it&apos;s typically a&lt;br/&gt;
bad idea to accept such a specific class in your constructor/setter.&lt;br/&gt;
In my case, I&apos;m creating my domain models with Immutables, so the&lt;br/&gt;
generated immutable class will override my getter that returns&lt;br/&gt;
`List&amp;lt;T&amp;gt;` to return `ImmutableList&amp;lt;T&amp;gt;`.&lt;/p&gt;

&lt;p&gt;The second issue was that various collections were boxed to specific&lt;br/&gt;
implementations like ArrayList/HashMap/HashSet. Doing this in TypeData&lt;br/&gt;
caused quite a few problems. The main being it was no longer possible to&lt;br/&gt;
understand the type hierarchy (ImmutableList does not extend ArrayList,&lt;br/&gt;
but it safely implements List). The ArrayList/HashMap/etc selection&lt;br/&gt;
happens in collection codec as an implementation detail. I think this is&lt;br/&gt;
a bit cleaner.&lt;/p&gt;

&lt;p&gt;3. It was not possible to use immutable collections like Guava&apos;s&lt;br/&gt;
ImmutableList. By fixing the above issues, it works now. Note that we&lt;br/&gt;
still can&apos;t use ImmutableList as an argument that&apos;s accepted, but that&apos;s&lt;br/&gt;
a case where it&apos;s reasonable to provide a custom codec implementation&lt;br/&gt;
since there&apos;s no way we can do that automatically.&lt;/p&gt;

&lt;p&gt;4. @BsonCreator annotations were only searched for in the target class,&lt;br/&gt;
but not any super classes. It&apos;s relatively common to have an abstract&lt;br/&gt;
base class host all of the static factory methods for the concrete&lt;br/&gt;
subtypes, so this allows for that pattern. I do the above, but in&lt;br/&gt;
addition, some of my domain models are generated through Immutables&lt;br/&gt;
where I don&apos;t have the ability to inject annotated static factory&lt;br/&gt;
methods.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I moved code around a bit for better locality and some re-usability. In&lt;br/&gt;
particular with PropertyReflectionUtils, and TypeData having a few&lt;br/&gt;
methods of extracting the types out of methods/fields/etc.&lt;/p&gt;

&lt;p&gt;I tried to add test cases for all of the above things - they all failed&lt;br/&gt;
prior to these modifications. All other existing bson tests pass.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jira.mongodb.org/browse/JAVA-2612&quot; title=&quot;Improve POJO Codec - support sub-classes with the bson creator&quot; class=&quot;issue-link&quot; data-issue-key=&quot;JAVA-2612&quot;&gt;&lt;del&gt;JAVA-2612&lt;/del&gt;&lt;/a&gt;&lt;br/&gt;
Branch: 3.6.x&lt;br/&gt;
&lt;a href=&quot;https://github.com/mongodb/mongo-java-driver/commit/275513a0ae355039aad81664c23d0c1d5ceb7aa5&quot; class=&quot;external-link&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener&quot;&gt;https://github.com/mongodb/mongo-java-driver/commit/275513a0ae355039aad81664c23d0c1d5ceb7aa5&lt;/a&gt;&lt;/p&gt;</comment>
                            <comment id="1741267" author="ross@10gen.com" created="Mon, 4 Dec 2017 09:47:40 +0000"  >&lt;p&gt;A massive thank you to Joseph Florencio for the excellent pull request.&lt;/p&gt;</comment>
                            <comment id="1741261" author="xgen-internal-githook" created="Mon, 4 Dec 2017 09:39:07 +0000"  >&lt;p&gt;Author:&lt;/p&gt;
{&apos;username&apos;: &apos;jflorencio&apos;, &apos;email&apos;: &apos;joe@deal.com&apos;, &apos;name&apos;: &apos;Joseph Florencio&apos;}
&lt;p&gt;Message: PojoCodec improvents for sub-classes&lt;/p&gt;

&lt;p&gt;Pojo Codec fixes with sub-classes and bson creator&lt;/p&gt;

&lt;p&gt;This commit tries to fix a few issues:&lt;/p&gt;

&lt;p&gt;1. It was not possible to overload a getter in a sub-class and make the&lt;br/&gt;
type more specific (covariant overload). For example, attempting to&lt;br/&gt;
serialize Bar would fail:&lt;/p&gt;

&lt;p&gt;```&lt;br/&gt;
  // Assume there are setters here as well...&lt;br/&gt;
  class Foo {&lt;br/&gt;
    public Number getNumber() &lt;/p&gt;
{ /*...*/ }&lt;br/&gt;
  }&lt;br/&gt;
&lt;br/&gt;
  class Bar extends Foo {&lt;br/&gt;
    @Override&lt;br/&gt;
    public Long getNumber() { /*...*/ }
&lt;p&gt;  }&lt;br/&gt;
```&lt;/p&gt;

&lt;p&gt;This was due to bridge methods not getting picked up, so even though&lt;br/&gt;
Method#getDeclaredMethods is documented to return methods only from the&lt;br/&gt;
super class, the Java compiler inserts a bridge method in the sub-class&lt;br/&gt;
(Bar in this case) for these covariant overrides for legacy interop.&lt;/p&gt;

&lt;p&gt;2.a. It was not possible to use a more specific type in a getter, but a&lt;br/&gt;
more general type in the setter/constructor/static factory method. For example:&lt;/p&gt;

&lt;p&gt;```&lt;br/&gt;
class Foo {&lt;br/&gt;
  public ImmutableList&amp;lt;String&amp;gt; getNames() &lt;/p&gt;
{ /*...*/ }&lt;br/&gt;
  // Mutable class&lt;br/&gt;
  public void setNames(List&amp;lt;String&amp;gt; names) { /*...*/ }
&lt;p&gt;  // or immutable class (both didn&apos;t work)&lt;br/&gt;
  @BsonCreator&lt;br/&gt;
  public Foo(@BsonProperty(&quot;names&quot;) List&amp;lt;String&amp;gt; names) &lt;/p&gt;
{ /*...*/ }
&lt;p&gt;}&lt;br/&gt;
```&lt;/p&gt;

&lt;p&gt;The first issue was that strict type equality was enforced. This is a&lt;br/&gt;
pretty common pattern that should be allowed though - it&apos;s typically a&lt;br/&gt;
bad idea to accept such a specific class in your constructor/setter.&lt;br/&gt;
In my case, I&apos;m creating my domain models with Immutables, so the&lt;br/&gt;
generated immutable class will override my getter that returns&lt;br/&gt;
`List&amp;lt;T&amp;gt;` to return `ImmutableList&amp;lt;T&amp;gt;`.&lt;/p&gt;

&lt;p&gt;The second issue was that various collections were boxed to specific&lt;br/&gt;
implementations like ArrayList/HashMap/HashSet. Doing this in TypeData&lt;br/&gt;
caused quite a few problems. The main being it was no longer possible to&lt;br/&gt;
understand the type hierarchy (ImmutableList does not extend ArrayList,&lt;br/&gt;
but it safely implements List). The ArrayList/HashMap/etc selection&lt;br/&gt;
happens in collection codec as an implementation detail. I think this is&lt;br/&gt;
a bit cleaner.&lt;/p&gt;

&lt;p&gt;3. It was not possible to use immutable collections like Guava&apos;s&lt;br/&gt;
ImmutableList. By fixing the above issues, it works now. Note that we&lt;br/&gt;
still can&apos;t use ImmutableList as an argument that&apos;s accepted, but that&apos;s&lt;br/&gt;
a case where it&apos;s reasonable to provide a custom codec implementation&lt;br/&gt;
since there&apos;s no way we can do that automatically.&lt;/p&gt;

&lt;p&gt;4. @BsonCreator annotations were only searched for in the target class,&lt;br/&gt;
but not any super classes. It&apos;s relatively common to have an abstract&lt;br/&gt;
base class host all of the static factory methods for the concrete&lt;br/&gt;
subtypes, so this allows for that pattern. I do the above, but in&lt;br/&gt;
addition, some of my domain models are generated through Immutables&lt;br/&gt;
where I don&apos;t have the ability to inject annotated static factory&lt;br/&gt;
methods.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I moved code around a bit for better locality and some re-usability. In&lt;br/&gt;
particular with PropertyReflectionUtils, and TypeData having a few&lt;br/&gt;
methods of extracting the types out of methods/fields/etc.&lt;/p&gt;

&lt;p&gt;I tried to add test cases for all of the above things - they all failed&lt;br/&gt;
prior to these modifications. All other existing bson tests pass.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jira.mongodb.org/browse/JAVA-2612&quot; title=&quot;Improve POJO Codec - support sub-classes with the bson creator&quot; class=&quot;issue-link&quot; data-issue-key=&quot;JAVA-2612&quot;&gt;&lt;del&gt;JAVA-2612&lt;/del&gt;&lt;/a&gt;&lt;br/&gt;
Branch: master&lt;br/&gt;
&lt;a href=&quot;https://github.com/mongodb/mongo-java-driver/commit/275513a0ae355039aad81664c23d0c1d5ceb7aa5&quot; class=&quot;external-link&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener&quot;&gt;https://github.com/mongodb/mongo-java-driver/commit/275513a0ae355039aad81664c23d0c1d5ceb7aa5&lt;/a&gt;&lt;/p&gt;</comment>
                    </comments>
                    <attachments>
                    </attachments>
                <subtasks>
                    </subtasks>
                <customfields>
                                                                                                                                                                                                                                                                                                                                                                    <customfield id="customfield_15850" key="com.atlassian.jira.plugins.jira-development-integration-plugin:devsummary">
                        <customfieldname>Development</customfieldname>
                        <customfieldvalues>
                            
                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    <customfield id="customfield_12550" key="com.pyxis.greenhopper.jira:gh-lexo-rank">
                        <customfieldname>Rank</customfieldname>
                        <customfieldvalues>
                            <customfieldvalue>2|ht7elz:</customfieldvalue>

                        </customfieldvalues>
                    </customfield>
                                                                <customfield id="customfield_10558" key="com.pyxis.greenhopper.jira:gh-global-rank">
                        <customfieldname>Rank (Obsolete)</customfieldname>
                        <customfieldvalues>
                            <customfieldvalue>9223372036854775807</customfieldvalue>
                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            </customfields>
    </item>
</channel>
</rss>