Videos

Maven enhancement

Enhancement of entity and query beans using the maven enhancement tile

IntelliJ plugin

Enhancement using the IntelliJ IDEA plugin

IntelliJ debugger

Looks at Idea debugger setting that invokes lazy loading

Eclipse plugin

Enhancement using the Eclipse plugin

Eclipse APT

Eclipse setup for Query bean generation (via Java annotation processor).

Recommendation

Use both IDE plugin enhancement (Idea or Eclipse) plus build time enhancement.

For IntelliJ IDEA and Eclipse users my recommendation is to use the IDE plugin in addition to either build time enhancement (maven plugin) or agent.

That is, the IDE plugin is good to use during development enhancing the beans as the IDE compiles the classes. Using the IDE plugin can reduce enhancement hassles during development and unit testing. For production you can then choose between build time enhancement via say maven or run time enhancement via javaagent.

Overview

The term "Enhancement" covers all the ways (javaagent, ant, maven,IDE plugin, etc) that are used to modify the entity beans. Others terms used to describe enhancement include "Weaving", "Transformation" and "byte code manipulation".

Load time weaving is used to refer to when the class manipulation occurs at "load time" typically via javaagent compared with class manipulation occurring at "build time" typically via Maven, Ant or IDE plugin.

In raw terms a class is a byte[] and enhancement is manipulating those bytes prior to the class being defined by its ClassLoader.

Using multiple enhancers

Ebean enhancement is aware of when the enhancement has already occurred. In this way it is common and expected that multiple forms of enhancement can be used at the same time such as using both an IDE plugin and maven plugin to perform the enhancement.

Maven enhancement

A Maven plugin performs the enhancement as part of the maven compile process. This can be described as "build time enhancement".

Maven enhancement tile

Ebean supplies a maven tile that brings in both the enhancer for entity beans as well as the enhancement for query beans. This is simpler, cleaner less verbose than defining the plugin in the traditional (non-tile) fashion and now the preferred approach.

The ebean enhancement tile brings plugin configuration for:
  • Entity bean and @Transactional enhancement for main and test
  • Query bean enhancement for main and test
  • ebean-codegen plugin, helper to generate finders etc
ebean enhancement tile
<plugin>
  <groupId>io.repaint.maven</groupId>
  <artifactId>tiles-maven-plugin</artifactId>
  <version>2.40</version>
  <extensions>true</extensions>
  <configuration>
    <tiles>
      <!-- other tiles ... -->
      <tile>io.ebean.tile:enhancement:14.1.0</tile>
    </tiles>
  </configuration>
</plugin>

Use mvn help:effective-pom to view the plugins that the tile brings in.

Maven plugin

If you do not want to use maven tiles you can specify the maven enhancement plugin in 'normal' fashion. Note the unlike the tile this does not bring in the query bean enhancement.

<plugin>
  <groupId>io.ebean</groupId>
  <artifactId>ebean-maven-plugin</artifactId>
  <version>${version}</version>
  <executions>
    <execution>
      <id>main</id>
      <phase>process-classes</phase>
      <configuration>
        <transformArgs>debug=1</transformArgs>
      </configuration>
      <goals>
        <goal>enhance</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Eclipse + Maven enhancement

Eclipse can be configured to enhance the classes during build using maven configuration

<build>
  <plugins>
    <!-- plugins here -->
  </plugins>
  <pluginManagement>
    <plugins>
      <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. -->
      <plugin>
        <groupId>org.eclipse.m2e</groupId>
        <artifactId>lifecycle-mapping</artifactId>
        <version>1.0.0</version>
        <configuration>
          <lifecycleMappingMetadata>
            <pluginExecutions>
              <pluginExecution>
                <pluginExecutionFilter>
                  <groupId>io.ebean</groupId>
                  <artifactId>ebean-maven-plugin</artifactId>
                  <versionRange>[${version},)</versionRange>
                  <goals>
                    <goal>enhance</goal>
                  </goals>
                </pluginExecutionFilter>
                <action>
                  <execute>
                    <runOnConfiguration>true</runOnConfiguration>
                    <runOnIncremental>true</runOnIncremental>
                  </execute>
                </action>
              </pluginExecution>
            </pluginExecutions>
          </lifecycleMappingMetadata>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

Agent

You can use javaagent parameter on the command line as per the examples below. This is often described as "Runtime enhancement" or "Load time enhancement".

Note that since version 4.7.1 of ebean-agent is expected that the agent will perform well without specifying the packages to enhance. That is, the agent is pretty good at ignoring non-interesting classes (JDK, groovy, scala, kotlin, jdbc drivers and many common libraries from apache, google, testing etc.

java -javaagent:ebean-agent-${version}.jar MyApplication
java -javaagent:ebean-agent-${version}.jar=debug=3 MyApplication

The ebean-agent jar can be downloaded from maven. The current version is:

<dependency>
  <groupId>${groupid}</groupId>
  <artifactId>${artifactid}</artifactId>
  <version>${version_str}</version>
</dependency>
<dependency org="${groupid}" name="${artifactid}" rev="${version_str}"/>
@Grapes(
  @Grab(group='${groupid}', module='${artifactid}', version='${version_str}')
)
'${groupid}:${artifactid}:${version_str}'
'${groupid}:${artifactid}:${version_str}'
libraryDependencies += "${groupid}" % "${artifactid}" % "${version_str}"
[${groupid}/${artifactid} "${version_str}"]

Agent Loader

Agent Loader is NOT recommended

The agent loader can be used to programmatically load the javaagent onto a running JVM. However, this very occasionally does not enhance a bean as an entity bean class somehow gets loaded prior to the agent being loaded on the JVM and for this reason this is not a recommended approach.

Note that this approach is used in the testing code for Ebean itself and if you use this approach you need to make sure the agent loading occurs before any class loading of entity beans or transactional beans occurs.

<dependency>
  <groupId>${groupid}</groupId>
  <artifactId>${artifactid}</artifactId>
  <version>${version_str}</version>
</dependency>
<dependency org="${groupid}" name="${artifactid}" rev="${version_str}"/>
@Grapes(
  @Grab(group='${groupid}', module='${artifactid}', version='${version_str}')
)
'${groupid}:${artifactid}:${version_str}'
'${groupid}:${artifactid}:${version_str}'
libraryDependencies += "${groupid}" % "${artifactid}" % "${version_str}"
[${groupid}/${artifactid} "${version_str}"]

The code below loads the enhancer agent programmatically onto the running JVM.

import org.avaje.agentloader;
...
public void someApplicationBootupMethod() {
  // Load the agent into the running JVM process
  if (!AgentLoader.loadAgentFromClasspath("ebean-agent","debug=1;packages=org.example.model")) {
    logger.info("ebean-agent not found in classpath - not dynamically loaded");
  }
}

Ant enhancement

Modify your ant build.xml file to:

  1. Define the AntEnhanceTask.
  2. Create a target that uses the AntEnhanceTask to enhance the entity classes.
<taskdef
    name="ebeanEnhance"
    classname="io.ebean.enhance.ant.AntEnhanceTask"
    classpath="your_path_to/ebean-x.x.x.jar"/>

<target name="ormEnhance" depends="clean,compile">
  <!--
  classSource: This is the directory that contains your class files. That is, the directory where your IDE will compile your java class files to, or the directory where a previous ant task will compile your java class files to.

  packages: a comma delimited list of packages that contain entity classes. All the classes in these packages are searched for entity classes to be enhanced. transformArgs: This contains a debug level (0 - 10) .
  -->
  <ebeanEnhance
      classSource="your_classes_directory"
      packages="app.domain.*"
      transformArgs="debug=1"/>
</target>

Programmatic enhancement

You can run the enhancement yourself programmatically using the Transformer class from the agent.

Transformer transformer = new Transformer("", "debug=1");
InputStreamTransform streamTransform = new InputStreamTransform(transformer, Ebean.class.getClassLoader());

// Or use ASM or Javassist get bytecode and new InputSream
// eg. in = new ByteArrayInputStream(ctClass.toBytecode());
InputStream in = new URL("your.class.file.class").openStream();

byte[] result = null;
try {
  result = streamTransform.transform("your.class.name", in);//class must be not loaded by classLoader
} finally {
  in.close();
}
if (result == null) {
  throw new CannotCompileException("ebean enhance model fail!");
}
defineClass("your.class.name", result, 0, result.length);