spring-projects / spring-loaded

Java agent that enables class reloading in a running JVM
Apache License 2.0
2.72k stars 515 forks source link

anonymous lambda expressions in excluded classes #111

Open ilx opened 9 years ago

ilx commented 9 years ago

Hi, I don't know if the title is correct, so I will try to describe issue.

I've added HikarcCP packages to exclusions but I was surprised when I got:

java.lang.IllegalStateException: The type registry TypeRegistry(id=1875062000,loader=sun.misc.Launcher$AppClassLoader) does not know about type id 789 (run(Lcom/zaxxer/hikari/pool/PoolBagEntry;)Ljava/lang/Runnable;)
    at org.springsource.loaded.TypeRegistry.getReloadableType(TypeRegistry.java:1694) ~[agent-0.13-SNAPSHOT.jar:1.2.3.BUILD-SNAPSHOT]
    at org.springsource.loaded.TypeRegistry.idyrun(TypeRegistry.java:1576) ~[agent-0.13-SNAPSHOT.jar:1.2.3.BUILD-SNAPSHOT]
    at com.zaxxer.hikari.pool.HikariPool.closeConnection(HikariPool.java:382) ~[HikariCP-2.2.5.jar:na]
    at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:183) ~[HikariCP-2.2.5.jar:na]
    at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:91) ~[HikariCP-2.2.5.jar:na]
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:630) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:695) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:727) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:737) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
    at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:811) ~[spring-jdbc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
    at org.springframework.security.oauth2.provider.token.store.JdbcTokenStore.readAccessToken(JdbcTokenStore.java:160) ~[spring-security-oauth2-2.0.5.RELEASE.jar:na]

If I take a look at the relevant code it's:

executor:

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

HikariCP:

   private void closeConnection(final PoolBagEntry bagEntry)
   {
      int tc = totalConnections.decrementAndGet();
      connectionBag.remove(bagEntry);
      if (tc < 0) {
         LOGGER.warn("Internal accounting inconsistency, totalConnections={}", tc, new Exception());
      }
      closeConnectionExecutor.submit(() -> { quietlyCloseConnection(bagEntry.connection); });
   }

so, it seems that closeConnection is using lambda expression from excluded package.

Btw. in order to capture nameAndDescriptor I had to modify TypeRegistry public static method:

  @UsedByGeneratedCode
  public static ReloadableType getReloadableType(int typeRegistryId, int typeId) {
    return getReloadableType(typeRegistryId, typeId, null);
  }

  public static ReloadableType getReloadableType(int typeRegistryId, int typeId, String nameAndDescriptor) {
    if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
      log.info(">TypeRegistry.getReloadableType(typeRegistryId=" + typeRegistryId + ",typeId=" + typeId + ")");
    }
    TypeRegistry typeRegistry = registryInstances[typeRegistryId].get();
    if (typeRegistry == null) {
      throw new IllegalStateException("Request to access registry id " + typeRegistryId + " but no registry with that id has been created for type " + nameAndDescriptor);
    }
    ReloadableType reloadableType = typeRegistry.getReloadableType(typeId);
    if (reloadableType == null) {
      throw new IllegalStateException("The type registry " + typeRegistry + " does not know about type id " + typeId + " ("+nameAndDescriptor+")");
    }
    reloadableType.setResolved();
    if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
      log.info("<TypeRegistry.getReloadableType(typeRegistryId=" + typeRegistryId + ",typeId=" + typeId + ") returning " + reloadableType);
    }
    reloadableType.createTypeAssociations();
    return reloadableType;

  }

does this mean that exclusion is not working properly for lambdas or is this something completely different?

ilx commented 9 years ago

invocation:

-noverify
-javaagent:/home/ilx/.m2/m2repo/org/springframework/springloaded/1.2.3.BUILD-SNAPSHOT/springloaded-1.2.3.BUILD-SNAPSHOT.jar
-Dspringloaded=plugins=io.github.jhipster.loaded.instrument.JHipsterLoadtimeInstrumentationPlugin,io.github.jhipster.loaded.JHipsterPluginManagerReloadPlugin;exclusions=com.zaxxer.hikari..*,org.hibernate..*,org.springframework.orm.jpa..*
-DhotReload.enabled=true
...
ilx commented 9 years ago

Seems that exclusion is important factor here. If type is not reloaded idyrun will be invoked and it will fail.

aclement commented 9 years ago

I can imagine exclusions are not working with invokedynamic, there are no tests for that scenario and whether exclusions were occurring was not considered in the quick prototype effort that got reloading of invokedynamic going.