Run and Develop with Cargo
Introduction
Goal
Use Cargo to run your Bloomreach Experience Manager implementation project in your development environment.
What is Cargo
Cargo is a thin wrapper that allows you to control Java EE containers in a standard way. In development environments, we recommend Cargo to run the CMS and HST applications inside one and the same container from Maven. In the standard Maven POM that is provided with the archetype, the Maven Cargo plugin is pre-configured to run with Tomcat 9 out of the box. This page explains how to work with the default Cargo setup: how to run, how to debug, and how to customize the configuration.
Run with Cargo
For the purpose of running a Bloomreach Experience Manager project, i.e. a CMS instance with an embedded JCR repository together with an HST website, a dedicated Maven profile for Cargo is provided by the Bloomreach Experience Manager Project POM. The latter is a product-level base POM that all Bloomreach Experience Manager projects inherit from, whether they are core CMS modules or end-user projects. To view the Bloomreach Experience Manager Project POM and the Cargo configuration defined there, see the relevant release in the Bloomreach Experience Manager Maven repository. To learn about Maven profiles refer to the introduction to build profiles on the Maven documentation website.
Maven profiles can be activated explicitly with the -P switch of the mvn command, and this is how the Cargo profile should be activated as well. To run a Bloomreach Experience Manager project with Cargo from the command line type:
mvn -P cargo.run
Note that you can only run this command from the root directory of your project. Running this command for the first time will automatically download a Tomcat 9 distribution. It will then unpack Tomcat in target/cargo/installs/apache-tomcat-9.x.xx and copy it to target/tomcat9x, patching some Tomcat configuration files in the process. This instance then (i.e. target/tomcat9x) is the instance of Tomcat that will be started by Cargo. But before starting up, Cargo will copy your project's web applications and the shared libraries they need to the relevant places within this Tomcat installation. Web applications go into target/tomcat9x/webapps and shared libraries go into target/tomcat9x/shared/lib.
Debug with Cargo
The Bloomreach Experience Manager Cargo profile provides several useful options to debug your Bloomreach Experience Manager project. First of all debugging is enabled on port 8000 by default. You don't have to do anything extra to attach your favorite debugger when running a Bloomreach Experience Manager project. Refer to Develop with Eclipse to learn how to use the Eclipse built-in debugger for this.
By default Cargo starts up Tomcat without waiting for a debugger to be attached. In order to modify this behavior you can specify the property cargo.debug.suspend=y when running with the Cargo profile:
mvn -P cargo.run -Dcargo.debug.suspend=y
This is particularly useful if you want to debug something during the startup and initialization phases of the CMS or website. The JVM will now suspend execution until you have attached a debugger.
You can also specify an alternative port number for the remote debugging facility. This can be useful if the default port is already in use by another application. To specify an alternative port number for the remote debugging facility change the property cargo.debug.address.
mvn -P cargo.run -Dcargo.debug.address=9000
Note: for a faster development cycle refer to Using JRebel to develop your project.
Run with Cargo without Development Repository Data
A standard Bloomreach Experience Manager project based on the Maven archetype contains a repository data JAR modules called repository-data-development and repository-data-site-development.By default, repository-data-development and repository-data-site-development are deployed into Tomcat's shared/lib directory. The latter can optionally be omitted using the Maven profile without-development-data combined with the cargo.run profile:
mvn -P cargo.run,without-development-data
This will cause the repository-data-development and repository-data-site-development JARs not to be deployed. For example, this is useful when testing an upgraded project locally, using a copy of an existing production repository.
Run with Cargo using Different Port Numbers
By default, the Cargo plugin will open connectors on some default ports (8080 for HTTP connector, 8025 for RMI connector, and optionally AJP port at 8009).
If you want to change these default port numbers, then you can add the following properties in the cargo profile configuration like the following example:
<profile> <id>cargo.run</id> <build> <plugins> <plugin> <groupId>org.codehaus.cargo</groupId> <artifactId>cargo-maven3-plugin</artifactId> <configuration> <!-- SNIP --> <configuration> <properties> <!-- SNIP --> <cargo.servlet.port>9080</cargo.servlet.port> <cargo.rmi.port>9205</cargo.rmi.port> <cargo.tomcat.ajp.port>9009</cargo.tomcat.ajp.port> <!-- SNIP --> </properties> </configuration> <!-- SNIP --> </configuration> <!-- SNIP --> </plugin> </plugins> </build> </profile>
The above example properties will change the HTTP connector port number to 9080 (HTTP), RMI port to 9025 and AJP port to 9009.
Now, you can run it also with a debug address system property like the following example.
mvn -P cargo.run -Dcargo.debug.address=9000
You can also override the whole cargo JVM arguments with the debug address by adding the following properties:
<properties> <!-- SNIP --> <cargo.jvmargs>-agentlib:jdwp=transport=dt_socket,address=9000,server=y,suspend=${cargo.debug.suspend} -noverify ${javaagent} ${cargo.jvm.args}</cargo.jvmargs> <!-- SNIP --> </properties>
Then you can run it with the different HTTP port and debug address with the simple command line, too:
mvn -P cargo.run
Finally, to make sure the HST site implementation works properly, use the Console to update the delivery tier configuration to reflect the different HTTP port:
/hst:hst/hst:hosts/dev-localhost - hst:defaultport = 9080
Pass System Properties
To pass system properties to the application in your Cargo-based development environment, you have two options:
Either add them to the cargo plugin configuration in the primary pom:
<profile> <id>cargo.run</id> <build> <plugins> <plugin> <groupId>org.codehaus.cargo</groupId> <artifactId>cargo-maven3-plugin</artifactId> <configuration> <container> <systemProperties> <repo.config> file:${project.basedir}/conf/repository.xml </repo.config>
Or pass them on the command-line using the system property -Dcargo.jvm.args:
mvn -Pcargo.run -Dcargo.jvm.args="-Drepo.path=./storage"
Customize the Cargo profile
As mentioned before, the Cargo profile is defined by the Bloomreach Experience Manager Project POM. But if you set up your project using the archetype, this isn't the entire story. In fact, when you inspect the root POM of your project, first of all notice that it actually inherits from the Bloomreach Experience Manager Release POM and not from the Project POM directly. The Bloomreach Experience Manager Release POM is the common POM descriptor for Bloomreach Experience Manager end-user projects. It defines all the Bloomreach Experience Manager artifacts together with their version numbers that make up a specific release of the Bloomreach Experience Manager suite (repository artifacts, cms artifacts, addons, plugins, packages, etc.). On top of that, as a child of the Project POM, it also extends and adds configuration to the Cargo profile. To be more exact, all Bloomreach Experience Manager artifacts that are to end up in Tomcat's shared library location are specified in the Bloomreach Experience Manager Release POM. You can find the version of the Release POM that your project extends here.
The second thing to draw your attention to is that your project's root POM itself extends the Cargo profile in it's turn. Here the two wars that are to be deployed by Cargo are defined. The effective profile definition is therefore the result of the merge between the three mentioned profile definitions. Note that unfortunately the mvn help:effective-pom goal is broken when it comes to merging configurations and it won't show you the effective profile configuration maven internally holds.
Now, it will probably not happen very often, but there are cases where you would need to modify the Cargo profile configuration. Two typical cases include having another web application that you want to deploy besides the CMS and the HST site, and having a jar artifact that needs to be loaded by Tomcat's shared classloader.
The first case is the simplest and only involves adding another deployable configuration to the existing deployables configuration. Taking the existing deployables configuration as an example, this should be self-explanatory.
The second case is where you have code that needs to be accessed by two or more deployed web applications and where that code acts as a communication channel between these web applications. In that case you need to perform the same trick that is used by the Release POM in order to inject your jar file as a shared dependency inside the container configuration of the Cargo profile. Two cases must be distinguished: 1. the shared artifact is defined as an external dependency of your project, and 2. the shared artifact is produced by a submodule of your project.
As an example, imagine you have both these types of shared artifacts you want to deploy. One is a dependency with the Maven coordinates of org.example:mysharedartifact:jar, the other is an artifact that is produced by a submodule named shared that lives in a subdirectory of that same name of the project root directory. In this scenario the Cargo profile configuration that would successfully result in these shared artifacts to be loaded by the shared class loader would look similar to this:
<profile> <id>cargo.run</id> <build> <plugins> <plugin> <groupId>org.codehaus.cargo</groupId> <artifactId>cargo-maven3-plugin</artifactId> <configuration> <container> <dependencies> <!-- external shared dependency. must be defined in this POMs dependency section and scoped there as provided --> <dependency> <groupId>org.example</groupId> <artifactId>mysharedartifact</artifactId> <classpath>shared</classpath> </dependency> <!-- submodule artifact --> <dependency> <location> ${project.basedir}/shared/target/shared-${project.version}.jar </location> <classpath>shared</classpath> </dependency> </dependencies> </container> <!-- SNIP --> </configuration> </plugin> </plugins> </build> </profile>
The external shared dependency is defined in the usual Maven way by specifying its group and artifact ids. Don't forget to define this dependency as a project dependency as well, otherwise the cargo plugin won't be able to resolve it. Also, you must set this dependency's scope to provided in order to prevent it from being packaged with subproject wars. This would then result in the artifact being loaded by the web application class loader instead of by the shared class loader.
The dependency definition of the child module artifact looks a little different from the usual way Maven configures dependencies. This is because we must bypass Maven's dependency management resolution. After all, a submodule cannot be a dependency at the same time or we would have circularity. Instead we simply point to the built artifact in the submodule's target directory using the <location> element.
One last remark. Sometimes you need to have a dependency loaded by the container-wide common class loader. This is for instance the case you want to have Tomcat provide your application with a JNDI DataSource. For this Tomcat needs access to the JDBC driver you want to use. If you have such a container-wide dependency, you can use the extra class path: <classpath>extra</classpath>.