Salem
Salem

Reputation: 12986

Using PlayFramework + Ebean with Gradle

I'm trying to use the Play Gradle Plugin to compile/package a Play 2.3.x app that uses Ebean.

Everything works fine during compilation and packaging, but when I run the app I get the well known error

Entity type class SomeEntity is not an enhanced entity bean. 
Subclassing is not longer supported in Ebean

So how can I can make Gradle run the enhancer during compilation?

Upvotes: 5

Views: 1013

Answers (3)

Md Ayub Ali Sarker
Md Ayub Ali Sarker

Reputation: 11557

I was converting Play 2.5.2 (Java) project from sbt to gradle and facing the same problem, then tried with the solution given by @koolrich. but it did not work well. Every thing was fine but it failed to return data for relation object(it was returning null for relational object). Then i compared the enhanced bytecode generated by sbt and gradle, find out delta. Then find out how the play enhance the bytecode. play enhance bytecode in three steps.

  • It generates getters and setters for fields it they aren't any in place yet and is done by play-enhancements-plugins(play.core.enhancers.PropertiesEnhancer.generateAccessors)
  • It rewrites classes that directly access fields to use the accessors instead and is done by play-enhancements-plugins(play.core.enhancers.PropertiesEnhancer.rewriteAccess)
  • If using Ebean, the Ebean enhancer will be applied to the classes configured via application.conf (ebean-enhancement plugins)

Eaxmple:

Employee employee=Employee.find.byId(1);
Company company=employee.company;

After Step 1&2, this will be converted to

Company company=employee.getCompany();

With Employee#getCompany() being something like

@PropertiesEnhancer.GeneratedAccessor
public Company getCompany(){
return this.company;
}

After step 3, the getter will be modified to be something like

@PropertiesEnhancer.GeneratedAccessor
public Company getCompany(){
return _ebean_get_company();
}

protected Company _ebean_get_company() {
  this._ebean_intercept.preGetter("company");
  return this.company;
}

So converting sbt to gradle, you have to perform this three steps as gradle play plugins does not support this three steps. For step 3, ebean has enhancement class(ant Task) that can be used(solution given by @koolrich), for step 1 & 2, I wrote another enhancement ant Task which add accessor and rewrite access. here is gradle.build file look like.

configurations {
    enhance
    playEnhance
}

dependencies {
    enhance "org.avaje.ebeanorm:avaje-ebeanorm-agent:4.9.1"
    playEnhance 'com.typesafe.play:play-enhancer:1.1.0'
}

model {
    components {
        play {
            binaries.all{binary ->
                tasks.withType(PlatformScalaCompile) {
                    doLast {
                        ant.taskdef(name: "playenhancetask", classname:"com.xxx.gradlehelper.PlayGradleEnhancherTask", classpath:"${project.buildDir}/playBinary/classes/:${project.configurations.playEnhance.asPath}")
                        ant.playenhancetask(classSource: "${project.buildDir}/playBinary/classes", packages: 'com.xxx.xxx.*', classpath:"${project.configurations.play.asPath}")
                        ant.taskdef(name: 'ebean', classname: 'com.avaje.ebean.enhance.ant.AntEnhanceTask', classpath: project.configurations.enhance.asPath)
                        ant.ebean(classSource: "${project.buildDir}/playBinary/classes", packages: 'com.xxx.xxx.xxx.*', transformArgs: 'debug=1')
                    }
                }
            }
        }
    }
}

dependencies {
    play 'org.avaje:avaje-agentloader:2.1.2'
    play 'org.avaje.ebeanorm:avaje-ebeanorm:6.18.1'
    play 'com.typesafe.play:play-ebean_2.11:3.0.0'
    play 'com.typesafe.play:play-enhancer:1.1.0'
    play "org.avaje.ebeanorm:avaje-ebeanorm-agent:4.9.1"
    play group: 'org.apache.ant', name: 'ant', version: '1.8.2'
}

Here is my ant Task PlayGradleEnhancherTask.java

package com.xxx.gradlehelper;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import play.core.enhancers.*;

import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class PlayGradleEnhancherTask extends Task {

    String classpath;  
    String classSource;
    String transformArgs;
    String packages;
    public String getClasspath() {
        return classpath;
    }  
    public void setClasspath(String classpath) {
        this.classpath = classpath;
    }    
    public void setClassSource(String source) {
        this.classSource = source;
    }   
    public void setTransformArgs(String transformArgs) {
        this.transformArgs = transformArgs;
    }

    public void setPackages(String packages) {
        this.packages = packages;
    }  
    public void execute() {
        if (packages == null) {
            throw new BuildException("No message set.");
        }
        log("classSource: " + classSource + ", packages: " + packages + "\n classPath: "+ classpath);

        String dir = packages.trim().replace('.', '/');
        dir = dir.substring(0, dir.length() - 1);

        String dirPath = classSource + "/" + dir;
        File d = new File(dirPath);
        if (!d.exists()) {
            throw new RuntimeException("File not found " + dirPath + "  currentDir:" + new File(".").getAbsolutePath());
        }

        Path path = Paths.get(dirPath);
        List<File> fileNames = new ArrayList();
        List<File> files = getFiles(fileNames, path);

        //generate property accessor
        generateAccessors(files);

        //rewrite access
        rewriteAccess(files);
    }

    private void rewriteAccess(List<File> files) {
        for (File file: files) {
            try{
                 PropertiesEnhancer.rewriteAccess(classSource+":"+classpath,file);
            }catch (Exception e){
                String fileName = file == null ? "null" : file.getName() +",  e: "+ e.getMessage();
                System.err.println("Could not enhance[rewriteAccess]:"+fileName);
            }
        }
    }
    private void generateAccessors(List<File> files) {
        for (File file: files) {
            try{
                PropertiesEnhancer.generateAccessors(classSource+":"+classpath,file);
            }catch (Exception e){
                e.printStackTrace();
                String fileName = file == null ? "null" : file.getName();
                System.err.println("Could not enhance[generateAccessors]: "+fileName);
            }
        }
    }

    private List<File> getFiles(List<File> files, Path dir) {
        try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
            for (Path path : stream) {
                if(path.toFile().isDirectory()) {
                    getFiles(files, path);
                } else {
                    File file = path.toFile();
                    if(!file.getName().startsWith("Reverse")&& file.getName().endsWith(".class")) {
                        files.add(file);
                    }
                }
            }
        } catch(IOException e) {
            e.printStackTrace();
        }
        return files;
    }
}

Upvotes: 0

Yonz
Yonz

Reputation: 180

@koolrich, i had tried the solution and when it didn't compile i moved on, only later to find the only problem was the dbmodels/* expected path while my path was different.

Initially what seemed like magic and confusing jargon about enhancements, the following helped me understand what is going on: https://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/ref_guide_pc_enhance.html Essentially, enhancement is adding some more methods and properties to work with persistance.

Upvotes: 0

koolrich
koolrich

Reputation: 66

This is how i have done it. I am using play 2.4 but should be able to work for you.

First add a configuration in your build.gradle as follows -

configurations {
    enhance
}

Next add a dependency on ebeanorm agent as shown below:

dependencies {
    enhance group: 'org.avaje.ebeanorm', name: 'avaje-ebeanorm-agent', version: '4.5.3'
}

Ensure you have the required play dependencies in your build.gradle as shown below:

dependencies {
    play 'org.avaje:avaje-agentloader:2.1.2'
    play "org.avaje.ebeanorm:avaje-ebeanorm-agent:4.5.3"
}

Finally add the following to do the enhancement after the compile task has executed:

model {
    components {
        play {
            binaries.all{binary ->
                tasks.withType(PlatformScalaCompile) {
                    doLast {
                        ant.taskdef(name: 'ebean', classname: 'com.avaje.ebean.enhance.ant.AntEnhanceTask', classpath: project.configurations.enhance.asPath)
                        ant.ebean(classSource: "${project.buildDir}/playBinary/classes", packages: 'models.package.name', transformArgs: 'debug=1')

                    }
                }
            }
        }
    }

Upvotes: 5

Related Questions