Testcontainers-java: Problematic use of generic types

Created on 26 Oct 2016  路  10Comments  路  Source: testcontainers/testcontainers-java

Using TestContainers 1.1.6, the following code produces a compile time error:

private static final PostgreSQLContainer postgres =
        new PostgreSQLContainer("postgres:9.5.4")
                .withDatabaseName("test")
                .withEnv("PGDATA", CONTAINER_PGDATA)
                .withFileSystemBind(HOST_PGDATA.toString(), CONTAINER_PGDATA, BindMode.READ_WRITE);

Compilation error:

/Users/my-user-name/my-project/my/path/to/IntegrationTest.java:71: error: incompatible types: GenericContainer cannot be converted to PostgreSQLContainer
                    .withFileSystemBind(HOST_PGDATA.toString(), CONTAINER_PGDATA, BindMode.READ_WRITE)

The problem stems from the fact that the PostgreSQLContainer class starts like this, with a recursive bound on its generic type parameter (_脿 la_ class Enum<E extends Enum<E>>):

public class PostgreSQLContainer<SELF extends PostgreSQLContainer<SELF>> 
        extends JdbcDatabaseContainer<SELF>

So this is a generic class, but the documentation and example test case use it with no type parameter, as a raw type:

@Rule
public PostgreSQLContainer postgres = new PostgreSQLContainer();

And in this situation Java can't figure out that in my use of the withFileSytemBind() method, the SELF return type unifies with PostgreSQLContainer.

My workaround for this issue illustrates the fix:

// A dummy class whose sole purpose is to instantiate the `SELF`
// type parameter.
private static final class MyPostgreSQLContainer extends PostgreSQLContainer<MyPostgreSQLContainer> {
    public MyPostgreSQLContainer(String dockerImageName) {
        super(dockerImageName);
    }
}

private static final MyPostgreSQLContainer postgres =
        new MyPostgreSQLContainer("postgres:9.5.4")
                .withDatabaseName("test")
                .withEnv("PGDATA", CONTAINER_PGDATA)
                .withFileSystemBind(HOST_PGDATA.toString(), CONTAINER_PGDATA, BindMode.READ_WRITE);

This argues that the existing "end user facing" classes like PostgreSQLContainer in the library ought to be split into two classes:

public abstract class AbstractPostgreSQLContainer<SELF extends AbstractPostgreSQLContainer<SELF>> 
        extends JdbcDatabaseContainer<SELF> {
    ...
}

public class PostgreSQLContainer extends AbstractPostgreSQLContainer<PostgreSQLContainer> {
    ...
}

People who want to extend PostgreSQLContainer should be discouraged from doing so as well, and pointed to AbstractPostgreSQLContainer as the proper class for extension. (Making PostgreSQLContainer a final class is one alternative here, but with the downside that it's a backwards-incompatible change.)

stale typbreaking-api-change

Most helpful comment

This issue also makes it very hard to use Testcontainers in Kotlin (and still exists in the current 1.2.1 version).

Is there any progress on this? Can I help out with a PR?

All 10 comments

@ldcasillas-progreso thanks for the report!

As a temporary workaround, I can suggest this:

private static final PostgreSQLContainer postgres =
   new PostgreSQLContainer<>("postgres:9.5.4")
    .withDatabaseName("test")
    .withEnv("PGDATA", CONTAINER_PGDATA)
    .withFileSystemBind(HOST_PGDATA.toString(), CONTAINER_PGDATA, BindMode.READ_WRITE);

(note the diamond operator)

@bsideup Ah, good one!

@ldcasillas-progreso yes, I can see how this would be annoying, and we're keen to come up with a more user-friendly model (which will probably be a breaking change and the focus of a future 2.0.0 release).

In the meantime @bsideup's workaround is the right way, and I'd like to think about your suggested fix too. It looks pretty sensible to me. It might be a good solution for users on the 1.x.x series and something we can integrate reasonably soon.

This issue also makes it very hard to use Testcontainers in Kotlin (and still exists in the current 1.2.1 version).

Is there any progress on this? Can I help out with a PR?

Hi @mpecan,

If you have an idea how to solve self-referencing without breaking current behavior it would be great!

You can also submit a bug to Kotlin... Well... Because it's a bug in Kotlin and a valid JVM bytecode :D

The solution I've shared at https://github.com/testcontainers/testcontainers-java/issues/318#issuecomment-290692749 works without any issues for me.

@martin-g Agreed. And I can see why.

@bsideup I will give it a stab in the next couple of days.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you believe this is a mistake, please reply to this comment to keep it open. If there isn't one already, a PR to fix or at least reproduce the problem in a test case will always help us get back on track to tackle this.

This issue has been automatically closed due to inactivity. We apologise if this is still an active problem for you, and would ask you to re-open the issue if this is the case.

Reopen please. Should be an absolute top priority if you ask me.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aruizca picture aruizca  路  4Comments

oneiros-de picture oneiros-de  路  3Comments

micheal-swiggs picture micheal-swiggs  路  4Comments

naderghanbari picture naderghanbari  路  3Comments

chomhanks picture chomhanks  路  3Comments