Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

To enable multiple classloader mode, you need to tapestry.multiple-classloaders symbol to true and run the webapp with production mode disabled. With production mode on, tapestry.multiple-classloaders is ignored.

When multiple classloaders are enabled, when a class in a controlled component, template, message properties file or asset is changed, Tapestry tries to invalidate as few cached objects as possible. This include page instances, processed templates, asset information, property bindings, etc. This is possible by 2 new internal features:

...

The PCLC tree is created class by class, starting from pages and then on its dependencies on them in a recursive manner. For each class, if there's no PCLC already containing it, Tapestry checks its dependency list.  

Loading, Storing and Preloading Dependency Information

When a page instance is finished building, it's processed by the component dependency registry, which gathers the dependencies this page has, then the dependencies of its dependencies recursively. The registry doesn't process classes it already has information.

When starting up, Tapestry checks whether a file named tapestryComponentDependencies.json exists in the folder where the webapp is running. If it does, component dependency is loaded from it.

The T5Dashboard page has 2 new buttons related to dependencies, Store dependency information, writes all the dependency information as known at that moment.

Preload dependency information and page classloader contexts goes through all known pages, gathers its dependencies, also doing the same for all the components used directly and indirectly by them, and the preloads page the page classloader contexts, thus avoiding context invalidations when page instances are created. Notice this process won't work if any of the pages or their templates are invalid for any reason.

Caveats

Its basic algorithm is this:

  1. If there are dependencies that belong to a PCLC yet, run the algorithm on it.
  2. All the PCLCs of dependencies are gathered.
    1. If there are no PCLCs found, a new one is created with that class and the root context is its parent.
    2. If there is exactly one PCLC found, a new one is created with that class and the found PCLC will be its parent.
    3. If more than one PCLC is found, these PCLCs are merged by creating a new one containing all the classes in the PCLCs. The old PCLCs are invalidated recursively and an event about its invalidation is sent to the listeners. One of them is PageSource, the service that manages, creates and caches page instances, so the merging of PCLCs can cause page invalidation, as keeping these instances could potentially cause ClassCastExceptions later due to class instances belonging to different versions of the same class.

Loading, Storing and Preloading Dependency Information

When a page instance is finished building, it's processed by the component dependency registry, which gathers the dependencies this page has, then the dependencies of its dependencies recursively. The registry doesn't process classes it already has information.

When starting up, Tapestry checks whether a file named tapestryComponentDependencies.json exists in the folder where the webapp is running. If it does, component dependency is loaded from it.

The T5Dashboard page has 2 new buttons related to dependencies, Store dependency information, writes all the dependency information as known at that moment.

Preload dependency information and page classloader contexts goes through all known pages, gathers its dependencies, also doing the same for all the components used directly and indirectly by them, and the preloads page the page classloader contexts, thus avoiding context invalidations when page instances are created. Notice this process won't work if any of the pages or their templates are invalid for any reason.

Caveats

Unfortunately, it wasn't possible to make the multiple classloader mode to work with Unfortunately, it wasn't possible to make the multiple classloader mode to work with all situations and code. Some known issues:

  1. On Java 9 and later, classes belonging to one classloader cannot call package-private methods of other classes even when both are in the same package. The solution is to change the method visibility to public.
  2. If you don't have component dependency information already loaded for a given class, problems, usually in the form of a ClassCastException claiming an object of a given type cannot be assigned to a variable of the same type. The best way to deal with this is to use the T5Dashboard to write the component dependency information to a file and restart the webapp. In some cases, it helps to preload dependency information for all classes.
  3. later, classes belonging to one classloader cannot call package-private methods of other classes even when both are in the same package. The solution is to change the method visibility to public.
  4. If you don't have component dependency information already loaded for a given class, problems, usually in the form of a ClassCastException claiming an object of a given type cannot be assigned to a variable of the same type. The best way to deal with this is to use the T5Dashboard to write the component dependency information to a file and restart the webapp. In some cases, it helps to preload dependency information for all classes.
  5. If you start your webapp without component dependency information, page instances may be created and invalidated a few times even when they didn't have any changes due to the page classloader context creation. The solution for this is the same one as the above.
  6. When one asset is imported into a class through @Import, the class and the asset are considered associated. When an asset is changed and it's associated with at least one class, the classes associated with it, including pages, are invalidated. If an asset is changed and it's not associated with any class, then multiple classloader mode works the same as single classloader one, invalidating everythingIf you start your webapp without component dependency information, page instances may be created and invalidated a few times even when they didn't have any changes due to the page classloader context creation. The solution for this is the same one as the above.