close

Filter

loading table of contents...

Blueprint Developer Manual / Version 2404

Table Of Contents
CoreMedia / Third-party Dependencies

Each plugin has its own class loader and can bring its own third-party libraries. This gives you control over the versions of the third-party libraries you use and makes you independent of third-party version changes in the particular CoreMedia Content Cloud application, so that your plugin will reliably continue to work with CoreMedia Content Cloud updates. The plugin's libraries are included in the plugin ZIP file, where the plugin class loader discovers them and loads the classes from.

However, at runtime your extensions are integrated into the particular CoreMedia Content Cloud component which features the extension point. Technically, this requires some classes (especially the Spring Framework) to be shared with the application.

Moreover, some frameworks like slf4j have application wide aspects, which do not work with objects of classes from different class loaders. And last but not least, any CoreMedia Content Cloud classes must be shared with the application. Even if it would work to use a particular CoreMedia Content Cloud feature just like an independent third-party library, CoreMedia does not guarantee this for updates, so just don't do it.

The following subsections describe how you can handle these issues.

Dependencies in Practice

Plugin developers must handle the difference between plugin libraries and shared classes appropriately. You have to develop your plugin in a Maven project. Each extension has its own module. Add a dependencyManagement section with at least the following entries to your POM file:

  • The BOM that manages the extension point

  • The BOM that manages the shared classes for the particular application (Each application with extension points provides such a BOM.)

Now, if you add a new dependency to your POM file, check whether it is managed by this dependency management. If it is not, it does not need to be shared, and you can simply declare the version of your choice in the dependency. Otherwise, omit the version and set the dependency scope to provided. Scoping all shared dependencies as provided, you can easily use the maven-dependency-plugin and the maven-assembly-plugin to build the plugin zip file including the non-shared libraries.

For example, a POM file for a studio-server plugin looks like this:

<project>

  <!-- [...] -->

  <dependencyManagement>
    <dependencies>

      <!-- For the extension point, e.g. ContentHubAdapterFactory -->
      <dependency>
        <groupId>com.coremedia.cms</groupId>
        <artifactId>studio-server-core-bom</artifactId>
        <version>${studio-server.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <!--
        For the third-party classes that must be
        shared with the studio-server application
      -->
      <dependency>
        <groupId>com.coremedia.cms</groupId>
        <artifactId>studio-server-thirdparty-for-plugins-bom</artifactId>
        <version>${studio-server.version</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <!-- An independent third-party library, managed right here. -->
    <dependency>
      <groupId>com.sun.activation</groupId>
      <artifactId>jakarta.activation</artifactId>
      <version>1.2.2</version>
    </dependency>

    <!-- coremedia dependencies must always be shared. -->
    <dependency>
      <groupId>com.coremedia.cms</groupId>
      <artifactId>content-hub-api</artifactId>
      <scope>provided</scope>
    </dependency>

    <!--
      The Spring framework is managed by studio-server-thirdparty-for-plugins-bom,
      thus, it must be shared by the plugin.
    -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <!-- [...] -->

</project>
More Dependency Subtleties

Further dependency problems might occur when you try to link instances of plugin classes with objects of application classes. The following example illustrates this. Assume, that you want to use the FasterXML Jackson libraries in your plugin. Those are not managed in the studio-server-thirdparty-for-plugins-bom, so you add a normal dependency to your pom:

<dependencies>
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.1</version>
  </dependency>

  <dependency>
    <groupId>com.coremedia.cms</groupId>
    <artifactId>content-hub-api</artifactId>
    <scope>provided</scope>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <scope>provided</scope>
  </dependency>

  <!-- [...] -->

<dependency>

You code your plugin, you build your plugin, you run your plugin. Everything works fine.

The next change in your plugin involves Spring's MappingJackson2HttpMessageConverter:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

private HttpMessageConverter<Object> createMessageConverter() {
  ObjectMapper objectMapper = new ObjectMapper();
  MappingJackson2HttpMessageConverter mc = new MappingJackson2HttpMessageConverter();
  mc.setObjectMapper(objectMapper);
  return mc;
}

You code your plugin, you build your plugin, you run your plugin. Everything wor... but wait, what's this?

the class loader org.pf4j.PluginClassLoader @227b4be7 of the current class,
com/acme/my/famous/StudioPlugin,
and the class loader 'app' for the method's defining class,
org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter,
have different Class objects for the type
com/fasterxml/jackson/databind/ObjectMapper

The MappingJackson2HttpMessageConverter class is shared with the application (because the spring-web dependency has the scope provided) and is loaded by the application class loader. MappingJackson2HttpMessageConverter references ObjectMapper as a method argument, so the application class loader also loads the ObjectMapper class.

Your plugin declares an ordinary (scope compile) jackson-databind dependency. Thus, the plugin class loader also loads the ObjectMapper class, independently of the application. Now, you invoke MappingJackson2HttpMessageConverter.setObjectMapper. The argument is an instance of the plugin's ObjectMapper, but the method expects an instance of the application's ObjectMapper. These two classes are not assignment compatible, not even if the plugin and the application use the same version of the jackson-databind library! This causes the observed error.

The solution is to set the scope provided for the jackson-databind dependency, in order to share the ObjectMapper class with the application, even though jackson-databind is not managed by studio-server-thirdparty-for-plugins-bom. However, this has some drawbacks that you know already from the CoreMedia Extensions:

  • You are bound to the particular version of jackson-databind that is used by Spring, so you cannot use the new features of later versions.

  • Other CoreMedia Content Cloud versions may use other Spring/Jackson versions, which may differ in functionality and make your plugin less portable.

This combination of Spring and Jackson is just an example. You might have to use other libraries with scope provided too. CoreMedia refrained from adding all such transitive dependencies to the thirdparty-for-plugins BOMs, because of the following reasons:

  • The appropriate scope depends on the particular usage of the library. It is not generally provided. As long as you use a library only with other plugin classes, you can use a compile scope dependency.

  • Normally, BOMs do not list transitive dependencies explicitly, so CoreMedia adheres to this convention.

Due to the drawbacks mentioned above, CoreMedia recommends that you use scope compile for dependencies that are not managed by the thirdparty-for-plugins BOMs whenever possible, and only switch to provided when necessary.

Search Results

Table Of Contents
warning

Your Internet Explorer is no longer supported.

Please use Mozilla Firefox, Google Chrome, or Microsoft Edge.