Using Avro schema from different package - java

We use Avro as our serialization engine in a big Java project.
We use maven plug-in for auto-generating classes from the Avro schemas (we only use avsc, no IDL).
All works nice until we come to cross-packages references.
The plug-in has the option to import schema files using the import tag, which works fine within the package, but when we try to reference a schema outside the package it does not fine it.
This is the plug-in setting:
<build>
<plugins>
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.7.7</version>
<configuration>
<stringType>String</stringType>
</configuration>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/resources/</sourceDirectory>
<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
<fieldVisibility>PRIVATE</fieldVisibility>
<imports>
<import>${project.parent.basedir}/SomeOtherPackagePath/schema1.avsc</import>
<import>${project.basedir}/src/main/resources/schema2.avsc</import>
</imports>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
schema2.avsc contains a record called schema2, and is imported correctly, and all other schemas referencing schema2 are valid.
schema1.avsc contains a record called schema1, and is in another package, so schemas in this packages referencing to it fail when building the maven:
[ERROR] Failed to execute goal org.apache.avro:avro-maven-plugin:1.7.7:schema (default) on
project matching-common: Execution default of goal org.apache.avro:avro-maven-plugin:1.7.7:schema failed: "Schema1" is not a defined name. The type of the "schema1" field must be a defined name or a {"type": ...} expression.
Are we missing something?
Is there any way to use schemas from other packages at all?

Please add the SomeOtherPackagePath in the sourceDirectories like below:
<sourceDirectories>
<sourceDirectory>${project.basedir}/src/main/resources/</sourceDirectory>
<sourceDirectory>${project.parent.basedir}/SomeOtherPackagePath/</sourceDirectory>
</sourceDirectories>

Related

How to specify separately the path of the java file and the tokens file generated by Antlr?

I`m attempting to use antlr4 in my project to parse files in custom format. But when I use antlr4 maven plugin, I cannot find a parameter to set separately the path of tokens file and java file, as it can only set a common path.
Here`s the configuration of antlr4 maven plugin:
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>4.11.1</version>
<executions>
<execution>
<id>antlr</id>
<goals>
<goal>antlr4</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
<configuration>
<libDirectory>src/main/antlr4/imports</libDirectory>
<sourceDirectory>src/main/antlr4</sourceDirectory>
<outputDirectory>src/main/java</outputDirectory>
<listener>true</listener>
<visitor>true</visitor>
<treatWarningsAsErrors>true</treatWarningsAsErrors>
</configuration>
</plugin>
The grammar file test.g4 is in <sourceDirectory>/relativePath(src/main/antlr4/org/trance233/antlr/).
The .tokens file is in <outputDirectory>(src/main/java/), the .java file is in <sourceDirectory>/relativePath(src/main/java/org/trance233/antlr/).
Is there any way that can set the path separately? Such as the .tokens file above is put in src/main/resources, and the .java file is put in src/main/java/org/trance233/antlr/.
A configuration for antlr4 is usually very simple like this:
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>antlr4</goal>
</goals>
<configuration>
<visitor>true</visitor>
<listener>false</listener>
</configuration>
</execution>
</executions>
</plugin>
This assumes you have your grammar file in src/main/antlr4/<package>/test.g4 (default location). There is no need to configure such things etc. because by conventions the generated class will be available in target/generated-sources/antlr4/. Exactly in this directory you will find the *.tokens files. The imports directory is defined by default as src/main/antl4/imports. The default configuration for the antlr4-maven-plugin defines some very useful defaults and it is strongly recommended to follow those conventions.
I strongly recommmend NOT to configure the <outputDirectory>src/main/java</outputDirectory> because that will generate code in your src/ directory tree which can cause issues with your versions control system.
BTW: Binding the code generation to the phase compile is very bad because the code generation should be done before the compile phase. The generate-sources phase is exactly intended for such cases. This phase is used by default by the antlr4-maven-plugin.
Is there any way that can set the path separately?
No.
But you can remove the .tokens file: it is not needed at runtime for your generated parser classes.

Compiling a dynamically generated java class

We have a requirement where we have to generate a java pojo class dynamically using json schema.
Till now we are able to generate the class on the fly(jsonschema2pojo library), but unable to compile the class for further processing.
For compilation classloader should get the class but as the java class is getting generated dynamically, class loader does not get it and throws InvocationTargetException.
Is there any way of compiling the java package again like using some maven plugin or some other way?
Thanks,
Vaishali Mangwani
If you are using Maven then you can configure plugin. https://joelittlejohn.github.io/jsonschema2pojo/site/1.0.1/generate-mojo.html.
Which will build your POJOs.
For example in "pom.xml" we are using:
<plugins>
<plugin>
<groupId>org.jsonschema2pojo</groupId>
<artifactId>jsonschema2pojo-maven-plugin</artifactId>
<version>1.0.0-alpha4</version>
<configuration>
<sourceDirectory>${basedir}/schemas</sourceDirectory>
<outputDirectory>${basedir}/src/main/java</outputDirectory>
<targetPackage>com.package</targetPackage>
<useCommonsLang3>true</useCommonsLang3>
<includeAdditionalProperties>false</includeAdditionalProperties>
<includeConstructors>true</includeConstructors>
<useLongIntegers>true</useLongIntegers>
<inclusionLevel>ALWAYS</inclusionLevel>
<generateBuilders>true</generateBuilders>
<includes>
<include>**/*.json</include>
</includes>
</configuration>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
...
</plugin>
</plugins>
In our case we have POJO's as separate package/library, so it can be shared between projects.
If you need runtime compilation, then tools like https://github.com/OpenHFT/Java-Runtime-Compiler could help, but this gets more involved and from the looks it does not make sense in your case.

Maven - update artifactId before build

I need to deploy my project into artifactory. For this purpose I use maven-assembly-plugin together with artifactory-maven-plugin
only I can use for building of mvn is this CMD (small updates are possible):
mvn -e -B -U clean deploy -DskipIntegrationTests=false -DskipCoverageReport=false -Dservice_name=sample_service
What I can not do in mvn command is update service name. It will be always "sample_service" or some other constant which represent name of service
Because I do not know name of service (there could be more services) my base part of pom.xml looks like this (artifactId is created dynamically from property service_name):
<groupId>my.group.id</groupId>
<artifactId>${service_name}</artifactId>
<version>2.0.0-SNAPSHOT</version>
The problem is that parameter -Dservice_name will always consists "underscores". Because of conventions artifact has to consists "dashes" instead of "underscores".
Is there any way (some plugin for instance) how I can do something like this?
<groupId>my.group.id</groupId>
<artifactId>${service_name}.replaceAll("_","-")</artifactId>
<version>2.0.0-SNAPSHOT</version>
In short for property service_name I need replace underscores by dashes before building of artifact.
Thanks for answers.
This cannot be done.
Properties used inside <artifactId> can only be set through the command line. You have no chance to manipulate them in Maven. The only chance I see is to change the command line, so that you do the replacement before you send the parameter to Maven.
I found a solution for my problem. But I am not sure if it is correct way how to solve it. I used plugin gmaven-plugin
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<executions>
<execution>
<phase>pre-clean</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.getModel().setArtifactId(project.properties["service_name"].replaceAll('_', '-'))
project.getArtifact().setArtifactId(project.properties["service_name"].replaceAll('_', '-'))
</source>
</configuration>
</execution>
</executions>
</plugin>
After that I use maven-assembly plugin which upload data into artifactory. And this plugin read artifact id from instance "project.getArtifacts()" so I update it directly.
So I updated artifact id directly in maven instance.
As I say it is not 100 percent correct but in my case it helps
You can do this with the buildhelper plugin, it has a goal regex-property which can set a property based on an initial value (your service_name property) and a regular expression to replace with a replacement value.
Example from the usage page (adapted because the value used made no sense):
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>regex-property</id>
<goals>
<goal>regex-property</goal>
</goals>
<configuration>
<name>human.version</name>
<value>${project.version}</value>
<regex>-SNAPSHOT</regex>
<replacement> pre-release development version</replacement>
<failIfNoMatch>false</failIfNoMatch>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

Use Maven Replace Plugin to replace a phrase on a generated Java file inside Target/generated-sources

I spent hours on this problem, searching several Google and SO entries, I have some ideas but not getting the result.
I have a maven file that does something like this:
grab a jar containing JSON schemas, and unpack them.
Using the Maven Replacer plugin (v 1.5.3), replace a line in a schema file called “MySchema.json” as such:
”Hello” :
”HelloWorld” :
then Maven would use another plugin to compile a class called “converter.java” and runs this class to output a Java file based on “MySchema.json”. let’s call the generated Java file “MyPojo.java”.
Now, I want Maven to replace a line in “MyPojo.java”, but no matter what I do I cannot achieve this.
I tried:
include a separate replace plugin entry for step 4 after the plugin that converts schemas to Java, but ofcourse this caused Maven to complain about existing replace plugin with same artifact/group id from step 2.
Tried adding a separate execution id to the goal “replace” for second plugin, this is invalid for this plugin.
There is a parent project to my current project folder, I tried putting another replacer plugin in the parent POM and make the phase to be any of the “package”, “generate-resources”, “compile”, etc. did not work. Note: the phase for replacements inside “MySchema.json” (in my current project POM) is generate-sources.
give absolute path to the Java, it kept complaining that path does not exist. But I copied and pasted the path to the Java inside windows explorer address bar after it was generated and could read it from Windows explorer. Note that the generated Java file “MyPojo.java”, went under “target/generated-sources” which is sourced by a parent POM above this project using a Maven Helper plugin in parent POM, so this folder should be visible as a source for further compilation. That Maven Helper plugin has phase generate-sources.
Use with same result as above
In my current project (non-parent one) this is the POM code:
<build>
<!—execute a plugin grab schemas jar and unpack schemas-->
...
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>replace</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
<include>${project.basedir}/target/schemas/MySchema.json</include>
</includes>
<replacements>
<replacement>
<token>"Hello":</token>
<value>"Hello World":</value>
</replacement>
</replacements>
</configuration>
</plugin>
<!-- execute a Plugin for converting shcemas to POJO -->
. . .
</plugins>
</build>
</project>
You should be able to declare the plugin only once and run two replace execution at different Maven Build Lifecycle phases:
Before the Json -> POJO conversion
After the Json -> POJO conversion
So, translating that into could would result in something like:
<plugin>
<!-- (unique) plugin declaration -->
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>maven-replacer-plugin</artifactId>
<version>1.3.5</version>
<executions>
<!-- first execution: replace on json file -->
<execution>
<id>replace-for-json</id>
<phase>some-phase-before-conversion</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<filesToInclude>${project.basedir}/target/schemas/MySchema.json</filesToInclude>
<preserveDir>true</preserveDir>
<outputDir>target</outputDir>
<replacements>
<replacement>
<token>"Hello":</token>
<value>"Hello World (Json)":</value>
</replacement>
</replacements>
</configuration>
</execution>
<!-- second execution: replace on java file -->
<execution>
<id>replace-for-pojo</id>
<phase>some-phase-after-conversion</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<filesToInclude>${project.basedir}/target/generated-sources/MyPojo.java</filesToInclude>
<preserveDir>true</preserveDir>
<outputDir>target</outputDir>
<replacements>
<replacement>
<token>"Hello":</token>
<value>"Hello World (Java)":</value>
</replacement>
</replacements>
</configuration>
</execution>
</executions>
</plugin>
Source: Configuration for the maven-replacer-plugin on two separate executions

Managing JAXB-generated classes in a Maven project

I have a Maven-based project, in which I trying to add some JAXB classes automatically generated by the "jaxb2-maven-plugin" Maven plugin. However, my first cut has me in a circular dependency loop:
Because these JAXB classes aren't generated yet, my other sources which reference them have compilation errors.
Because those other sources have compilation errors, these JAXB classes don't get generated.
It seems like there are two obvious possibilities for solving this:
Comment-out the broken references, so that the project builds and the JAXB classes are automatically generated. Then copy those generated sources from /target into /src/main/java, so that references to them won't cause compilation errors.
Create an entirely separate project, consisting of nothing but the JAXB stuff. Include it as a dependency in my main project.
Am I missing something here? Option #1 seems flat-out ridiculous... that just can't be the manner in which people use JAXB. Option #2 seems more rational, but still rather inefficient and cumbersome. I really have to take on the overhead of an entirely separate project just to use JAXB?
Are there any more elegant approaches that developers use to reference JAXB-generated classes in the same project where the Maven plugin generates them?
UPDATE: By request, here is the relevant portion of my POM:
<build>
<plugins>
<plugin>
<!-- configure the compiler to compile to Java 1.6 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- The name of your generated source package -->
<packageName>com.mypackage</packageName>
</configuration>
</plugin>
</plugins>
</build>
When I run mvn clean package, I DO see my JAXB sources being generated beneath the /target subdirectory. However, those generated sources are not being automatically added to the classpath for the compile phase.
POST-RESOLUTION UPDATE: It turns out that my compilation issues had more to do with the fact that I was running in Eclipse, and its Maven integration has some issues with "jaxb2-maven-plugin". See this StackOverflow question for more detail on that issue and its resolution.
How did you configure your jaxb maven plugin? Normally it runs in the generate-sources lifecycle, which comes before the compile lifecycle. So your JAXB generated classes should already be there when your own code gets compiled, Maven puts them in target/generated-source and puts that folder on the classpath.
Edit:
This is my code we use at work (and which works as expected):
<plugin>
<groupId>com.sun.tools.xjc.maven2</groupId>
<artifactId>maven-jaxb-plugin</artifactId>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaDirectory>src/main/resources/<companyname>/xsd</schemaDirectory>
<includeSchemas>
<includeSchema>retrieval.xsd</includeSchema>
<includeSchema>storage.xsd</includeSchema>
</includeSchemas>
</configuration>
</plugin>
Apparently we use yet another jaxb plugin... (see also this thread: Difference of Maven JAXB plugins).
i would suggest you to split jaxb-generated classes (api) and your BL classes (implementation) to 2 maven projects with separate pom.xml for each, and the main root pom.xml with the compilation order. that way, you will be able to build api.jar, then maven will install it inside the local repo, and after that you can use it as the dependency of your implementation. so it will looks like:
-API\
--pom.xml - for api, jaxb generation
-IMPL\
--pom.xml - for impl, api dependency is here
pom.xml - main pom.xml with references to the projects above
Maybe try using the maven-jaxb2-plugin instead:
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.2</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
The answer from dfuse is correct, though. Either plugin should generate sources before compiling, and the result of the source generation will be on the classpath. I tested this with both plugins. Is it possible for you to post your schema, or at least the schema for the type that your code is failing to pick up on the classpath?

Categories