I am switching a play app from SBT to gradle and the routes and reverse routes scala files are not being generated with
import _root_.play.libs.F
which is causing
build/src/play/binary/routesScalaSources/controllers/ReverseRoutes.scala:260: not found: value F
def validate(accountId:F.Option[java.lang.Long]): Call = {
I am using gradle 3.5, play: '2.4.8', scala: '2.11' and java: '1.8'. Does anyone know if there is a compatibility issue with or some other known issue that is stopping the import from being added to the generated scala file?
** EDIT **
I found this class RoutesCompile with a method additionalImports but I can't find how to use it in the build.gradle file. (I am super new to gradle, more of a maven guy)
** EDIT 2 **
Based on the Javadoc in the RoutesCompile class it seems like I should be adding it to the model like this:
model {
components {
play {
platform play: '2.4.8', scala: '2.11', java: '1.8'
injectedRoutesGenerator = true
additionalImports = ['play.libs.F']
sources {
twirlTemplates {
defaultImports = TwirlImports.JAVA
source.srcDir "assets/views"
source.exclude "assets/stylesheets"
}
}
}
}
}
But I get the following error:
> Exception thrown while executing model rule: play { ... } # build.gradle line 147, column 9
> No such property: additionalImports for class: org.gradle.play.PlayApplicationSpec
I have finally found the answer to my issue. I needed to get the task and add the additional import that way.
model {
components {
play {
platform play: '2.4.8', scala: '2.11', java: '1.8'
injectedRoutesGenerator = true
tasks.withType(RoutesCompile) {
additionalImports = ['play.libs.F']
}
sources {
twirlTemplates {
defaultImports = TwirlImports.JAVA
source.srcDir "assets/views"
source.exclude "assets/stylesheets"
}
}
}
}
}
Related
i have two packages "../java/abc"(.java) and "../java/xyz"(.kt)
i need to exclude abc package content in dokka html document
basically i could see both the package are documented in HTML by dokka. after performing
'./gradlew dokkaHtml'
can anybody give me a solution where i can customize/limit the documentation only for 1 package?
i have plugin and dependency in project level build.gradel file
dependencies {
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.6.21"
}
plugins {
id 'org.jetbrains.dokka' version '1.6.21' apply false
}
my app.gredel file look like below
apply plugin: 'kotlin-parcelize'
apply plugin: 'org.jetbrains.dokka'
tasks.dokkaHtml.configure {
dokkaSourceSets {
configureEach {
perPackageOption {
matchingRegex.set("abc")
suppress.set(true)
}
}
}
}
and I have also tried the below syntax
tasks.dokkaHtml.configure {
dokkaSourceSets {
configureEach {
perPackageOption {
prefix = "abc"
suppress = true
}
}
}
}
Expectation: need to customize the documentation for only 1 package.
I've got a Java application with the following variable:
static final String MAGIC_VERSION = "SET_BY_MAGIC";
Using gradle, I want to replace this on runtime with my gradle project version, however I am having issues replacing the string.
This is my gradle file:
tasks {
processResources {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
from(sourceSets["main"].java) {
filter {
it.replace("SET_BY_MAGIC", version as String)
}
}
}
}
What am I doing wrong?
This is the error I get when I try to create the class under test
Could not find matching constructor for: com.pittacode.apihelper.json.JsonObjectFlattener()
groovy.lang.GroovyRuntimeException: Could not find matching constructor for: com.pittacode.apihelper.json.JsonObjectFlattener() at com.pittacode.apihelper.json.JsonObjectFlattenerTest.flatten json object with one nested object(JsonObjectFlattenerTest.groovy:12)
This is my test class
package com.pittacode.apihelper.json
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import spock.lang.Specification
class JsonObjectFlattenerTest extends Specification {
def classUnderTest = new JsonObjectFlattener()
def "flatten json object with one nested object"() {
given:
def jsonString = """
{
"1-1": 11,
"1-2": {
"2-1": "21"
},
"1-3": 13
}
"""
def jsonObject = JsonParser.parseString(json).getAsJsonObject()
when:
JsonObject result = classUnderTest.flatten(jsonObject)
then:
result.keySet().containsAll(["1-1", "1-2", "1-3", "2-1"])
}
}
I have a gradle project with one subproject and a module-info.java
plugins {
id "groovy"
id "application"
id "org.beryx.jlink" version "2.25.0"
id "org.javamodularity.moduleplugin" version "1.8.10"
}
repositories {
mavenCentral()
}
ext {
log4jVersion = "2.17.2"
}
dependencies {
implementation("com.jayway.jsonpath:json-path:2.7.0") {
exclude group: "com.fasterxml.jackson.core"
exclude group: "com.google.gson"
}
implementation("com.fasterxml.jackson.core:jackson-databind:2.13.2")
implementation("com.google.code.gson:gson:2.9.0")
implementation("org.apache.logging.log4j:log4j-api:${log4jVersion}")
runtimeOnly("org.apache.logging.log4j:log4j-core:${log4jVersion}")
annotationProcessor("org.apache.logging.log4j:log4j-core:${log4jVersion}")
runtimeOnly("org.apache.logging.log4j:log4j-slf4j-impl:${log4jVersion}")
testImplementation("org.codehaus.groovy:groovy:3.0.9")
testImplementation("org.spockframework:spock-core:2.0-groovy-3.0")
}
application {
mainClass = "com.pittacode.apihelper.Runner"
mainModule = "com.pittacode.apihelper"
}
tasks.named("test") {
useJUnitPlatform()
}
jlink {
forceMerge "log4j", "jackson"
// options = ["--bind-services"] // makes jre bigger but has everything, good way to test stuff
launcher {
name = "apihelper"
jvmArgs = ["-Dlog4j.configurationFile=./log4j2.xml", "-Dlog4j2.debug=false"]
}
jpackage {
if (org.gradle.internal.os.OperatingSystem.current().windows) {
installerOptions += ["--win-per-user-install", "--win-dir-chooser", "--win-menu", "--win-shortcut"]
imageOptions += ["--win-console"]
}
}
}
tasks.jlink.doLast {
copy {
from("src/main/resources")
into("$buildDir/image/bin")
}
}
This is the class
package com.pittacode.apihelper.json;
import com.google.gson.JsonObject;
public final class JsonObjectFlattener {
public JsonObjectFlattener() {
}
public JsonObject flatten(JsonObject o) {
return null;
}
}
The weird thing is that in another specification class if I try to initiate another object (unrelated) it seems to create it just fine. That one has parameters so I tried adding some in the flattener as well but didn't seem to make a difference
Well this was silly,
Like I mentioned I am using java modules and it seems that I need to export the packages that contain the classes I want to test.
module com.pittacode.apihelper {
requires jdk.crypto.ec; // needed for ssl communication
requires org.slf4j;
requires java.net.http;
requires java.sql;
requires com.google.gson;
requires json.path;
requires org.apache.logging.log4j;
requires com.fasterxml.jackson.databind;
exports com.pittacode.apihelper;
exports com.pittacode.apihelper.json; // <-- this is the missing line
}
I created a small project in Intellij Idea to mimic what I am seeing trying to migrate a large legacy codebase to OPENJDK16. Here is the directory structure:
$ tree
/cygdrive/c/projects/play
|--api
|----api.iml
|----src
|------module-info.java (module com.company.feature.apimodule)
|------com
|--------company
|----------feature
|------------ApiRunnable.java
|--home
|----home.iml
|----src
|------module-info.java (module com.company.feature.homemodule)
|------com
|--------company
|----------feature
|------------Runnable1.java
|--Modules
|----.idea (Idea stuff in here)
|----out
|------production
|--------api
|--------home
module com.company.feature.apimodule
{
exports com.company.feature;
}
package com.company.feature;
public interface ApiRunnable
{
public void play(String[] strings);
}
module com.company.feature.homemodule
{
requires com.company.feature.apimodule; //Error Module not found
// requires apimodule; //Error Module not found
// requires com.company.feature.ApiRunnable; //Error Module not found
}
package com.company.feature;
public class Runnable1 implements ApiRunnable
{
#Override
public void play(String[] strings)
{
int count = 1;
for (String str : strings)
{
System.out.println("String " + count++ + " " + str);
}
}
public static void main(String[] args)
{
Runnable1 myRun = new Runnable1();
myRun.play(args);
}
}
Note the directory structure is identical in api and home. This is intentional as my legacy codebase has this structure. This small example seems to duplicate the problems I am having and I can't figure out if the structure is the problem. It did compile and run fine using the unnamed-module.
Error #1 when doing a rebuild
C:\Projects\play\home\src\com\company\feature\Runnable1.java
java: package exists in another module: com.company.feature.apimodule
Does this mean that I can't have identical package trees in api and home?
Error #2
The homemodule cannot see the apimodule, I keep getting (Module not found) no matter what I put in the requires line.
Is this related to Error #1? If not, how do I fix this without resorting to the unnamed module for everything?
This problem was resolved by putting the module-info.java in the right directory. This is poorly documented and was found by trial and error.
The right directory is api:
module apimodule
{
exports com.company.feature;
}
and home:
module homemodule
{
requires apimodule;
}
It appears that once the module-info.java files are in place, the Runnable1.java doesn't need the import statements, since the imported package is the same as the local one.
There is plenty of features that already available on Jeta, but what if something is missing. Can I create my own annotations and generate metacode for them?
Needed a step-by-step tutorial how to create custom Jeta processors.
How to create custom processors, step-by-step tutorial
Step 1: Hello, World project
For this tutorial let's create a simple Gradle project with one module app and with a single class SayHelloApp. This class writes Hello, World! to standard output.
For the illustration we are going to create Hello annotation that sets Hello, Jeta! string to the annotated fields.
Step 2: common module
First, we need a module that will be accessible in app and apt (will create shortly) modules. In common module we need two classes - Hello annotation and HelloMetacode interface:
Step 3: apt module
apt - is a module in which we'll create all the required for code generation classes. For this tutorial we need a processor that will handle our Hello annotation.
Note that this module depends on common module so we used Hello annotation as a parameter for the super constructor. By doing that we're saying to Jeta that we need all the elements annotated with given type. The module also depends on jeta-apt in order to get access to the Jeta classes.
Step 4: Processor
Created SayHelloProcessor now does nothing. Let's add some logic in it. The idea here is to generate java code that sets Hello, Jeta string to the fields annotated with Hello.
Note that Jeta uses JavaPoet to create java source code. It's really great framework by Square. Please, check it out on GitHub.
First, we need that our metacode implements HelloMetacode. To do that we'll add super interface to the builder:
MetacodeContext context = roundContext.metacodeContext();
ClassName masterClassName = ClassName.get(context.masterElement());
builder.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(HelloMetacode.class), masterClassName));
Next, implement HelloMetacode by creating void setHello(M master) method:
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("setHello")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addParameter(masterClassName, "master");
Finally, the statements for each element annotated with Hello, that Jeta passes in process method via roundContext parameter:
for (Element element : roundContext.elements()) {
String fieldName = element.getSimpleName().toString();
methodBuilder.addStatement("master.$L = \"Hello, Jeta\"", fieldName);
}
Here is the complete SayHelloProcessor listing:
package org.brooth.jeta.samples.apt;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import org.brooth.jeta.apt.MetacodeContext;
import org.brooth.jeta.apt.RoundContext;
import org.brooth.jeta.apt.processors.AbstractProcessor;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
public class SayHelloProcessor extends AbstractProcessor {
public SayHelloProcessor() {
super(Hello.class);
}
#Override
public boolean process(TypeSpec.Builder builder, RoundContext roundContext) {
MetacodeContext context = roundContext.metacodeContext();
ClassName masterClassName = ClassName.get(context.masterElement());
builder.addSuperinterface(ParameterizedTypeName.get(
ClassName.get(HelloMetacode.class), masterClassName));
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("setHello")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addParameter(masterClassName, "master");
for (Element element : roundContext.elements()) {
String fieldName = element.getSimpleName().toString();
methodBuilder.addStatement("master.$L = \"Hello, Jeta\"", fieldName);
}
builder.addMethod(methodBuilder.build());
return false;
}
}
Step 5: Metacode
All the required for code generating classes are created and we're ready to try. But first, we need to add jeta.properties file in order to configurate Jeta. You can find more details about this file on this page. The file should be located in the root package. For our tutorial its content would be:
metasitory.package=org.brooth.jeta.samples
processors.add=org.brooth.jeta.samples.apt.SayHelloProcessor
Next, modify SayHelloApp. Instead of initializing text field we'll put Hello annotation on it:
public class SayHelloApp {
#Hello
String text;
}
And build.gradle:
group 'org.brooth.jeta-samples'
version '1.0'
buildscript {
repositories {
maven {
url 'https://plugins.gradle.org/m2/'
}
}
dependencies {
classpath 'net.ltgt.gradle:gradle-apt-plugin:0.5'
}
}
apply plugin: 'net.ltgt.apt'
apply plugin: 'java'
sourceCompatibility = 1.7
repositories {
mavenCentral()
jcenter()
}
compileJava {
options.sourcepath = files('src/main/java')
}
dependencies {
apt project(':apt')
compile project(':common')
compile 'org.brooth.jeta:jeta:+'
}
Now we're ready to generate metacode. Run next command in your console:
./gradlew assemble
If there is no problems so far, we'll see SayHelloApp_Metacode file under app/build directory:
Step 6: Controller
Controllers are the classes that apply metacode to the masters. Let's create one for HelloMetacode in app module:
package org.brooth.jeta.samples;
import org.brooth.jeta.MasterController;
import org.brooth.jeta.metasitory.Metasitory;
public class SayHelloController<M> extends MasterController<M, HelloMetacode<M>> {
public SayHelloController(Metasitory metasitory, M master) {
super(metasitory, master, Hello.class, false);
}
public void setHello() {
for (HelloMetacode<M> metacode : metacodes)
metacode.setHello(master);
}
}
Step 7: MetaHelper
MetaHelper is a simple static-helper class. You shouldn't use it in your project if you are not comfortable with static helpers. You can read more details about this class on this page.
Anyway, let's create MetaHelper in app module:
package org.brooth.jeta.samples;
import org.brooth.jeta.metasitory.MapMetasitory;
import org.brooth.jeta.metasitory.Metasitory;
public class MetaHelper {
private static MetaHelper instance;
private final Metasitory metasitory;
public static MetaHelper getInstance() {
if (instance == null)
instance = new MetaHelper("org.brooth.jeta.samples");
return instance;
}
private MetaHelper(String metaPackage) {
metasitory = new MapMetasitory(metaPackage);
}
public static void setHello(Object master) {
new SayHelloController<>(getInstance().metasitory, master).setHello();
}
}
Note that we must pass to MapMetasitory the same package ("org.brooth.jeta.samples") that we specified as metasitory.package in jeta.properties.
Step 8: Usage
The last step - we invoke our MetaHelper's method. Here is the complete listing of SayHelloApp:
package org.brooth.jeta.samples;
public class SayHelloApp {
#Hello
String text;
public SayHelloApp() {
MetaHelper.setHello(this);
}
public void sayHello() {
System.out.print(text);
}
public static void main(String[] args) {
new SayHelloApp().sayHello();
}
}
Finally, we can run SayHelloApp. In the console we should see:
Hello, Jeta
Links
This tutorial on GitHub
Jeta Website
Jeta on Android
Happy code-generating! :)