I was in the process of converting java projects build system from Ant to Maven and there are literally 700+ dependency jar files lying in a folder without any version or package information.
I was able to figure out maven co-ordinates for 400+ of those jar files using it's hash. So for the remaining 300+ jar files I am thinking of uploading it directly to a local repo in Artifactory and then generate maven co-ordinates automatically.
As far as i have explored the only way to achieve this is to deploy/upload every jar file manually via Artifactory UI with Deploy as Maven Artifact option enabled to generate co-ordinates automatically but this is a very time consuming process (I want to do this for 300+ files).
is there any other efficient way to do it?
I see two ways to achieve what you want, unfortunately none is available "out of the box"...
Use the command line client to upload each JAR file to Artifactory.
The main command to upload is:
jfrog rt upload foo.jar maven-local-repo
See https://www.jfrog.com/confluence/display/CLI/CLI+for+JFrog+Artifactory for more details
Use a bash script to loop on the JAR files, and for each file, upload it to a separate location, generate a short pom (from a sample pom and some sed to replace groupId and artifactId with the filename) and upload it next to the JAR file.
As Artifactory provides this option over its webapp, create a Selenium client that loops on each JAR file, connects to the Artifactory UI, and uploads each file using the "Generate default POM" option.
See https://www.jfrog.com/confluence/display/JFROG/Deploying+Artifacts#DeployingArtifacts-DeployingMavenArtifacts
I'm sure you already figured out a solution, but for anyone else who has to do something similar I ended up just using gradle to do it for me. I created a bare gradle project and with the following build.gradle. It collects all the jars from the directory specified and loops through them and creates publications for each. We wanted to use the sub-folder structure as the groupId so there's a bit of logic to format that in there.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.21.0"
}
}
apply plugin: 'java'
apply plugin: 'maven-publish'
apply plugin: "com.jfrog.artifactory"
version = '0.2021.0'
ext.thirdPartyLib = fileTree(dir: "$rootDir/../extrajars", include: ['**/*.jar'])
publishing {
publications {
thirdPartyLib.each{ jar->
def fbase = jar.name.minus(".jar")
"$fbase"(MavenPublication) {
artifact jar
artifactId fbase
//the following was to use the folder structure as the groupId
def path = jar.path.minus("\\" + jar.name)
path = path.replaceAll("\\\\", ".")
path = path.replaceAll("c:/pathtoDirectory", "")
groupId = path
}
}
}
}
artifactory {
contextUrl = 'http://yourArtifactoryUrl'
publish {
repository {
repoKey='yourRepo'
username='username'
password='password'
}
defaults {
thirdPartyLib.each{ jar->
def fbase = jar.name.minus(".jar")
publications( fbase )
}
}
}
}
Related
I can successfully add a generated openapi client to my project via source sets. But then I have to copy dependencies into the main build-gradle, resolve conflicts -> I think it would be a better design to have the client as a subproject with its own build.gradle.
So I add include = 'build:openapi-java-client' to my settings.gradle and compile project(':build:openapi-java-client') to my dependencies. So that I have the following files:
build.gradle:
plugins {
id 'java'
id 'application'
id "org.openapi.generator" version "4.3.1"
}
repositories {
jcenter()
}
openApiGenerate {
generatorName = "java"
inputSpec = "$rootDir/specs/petstore.yaml".toString()
outputDir = "$buildDir/openapi-java-client".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
configOptions = [
dateLibrary: "java8"
]
}
dependencies {
implementation 'com.google.guava:guava:29.0-jre'
testImplementation 'junit:junit:4.13'
compile project(':build:openapi-java-client')
}
application {
mainClassName = 'a.aa.App'
}
and settings.gradle:
rootProject.name = 'simple-java-app'
include = 'build:openapi-java-client'
I execute openApiGenerate in advance, after adding it as a subproject, I do Gradle -> Refresh Gradle Project and Refresh.
Eclipse then shows me a problem:
Could not run phased build action using Gradle distribution 'https://services.gradle.org/distributions/gradle-6.5.1-bin.zip'.
Settings file 'C:\...\simple-java-app\settings.gradle' line: 11
A problem occurred evaluating settings 'simple-java-app'.
Could not set unknown property 'include' for settings 'simple-java-app' of type org.gradle.initialization.DefaultSettings.
I don't know where to go from here, addressing subprojects in subfolders worked just fine when I worked through https://guides.gradle.org/creating-multi-project-builds/ and put greeting-library in a subfolder.
You are trying to make build/ a project when that directory specifically is not meant to be a project directory. It's Gradle default build directory and likely 99% of other plugins and other Gradle plugins.
Simply change output directory to something else other than build/:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$rootDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
Then include the project in your build with the correct syntax:
// settings.gradle
include("openapi-java-client")
However, using the org.openapi.generator seems to generate an invalid build.gradle since I get the following error:
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle' line: 23
* What went wrong:
Could not compile build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle'.
> startup failed:
build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle': 23: unexpected char: '\' # line 23, column 35.
main.java.srcDirs = ['src/main\java']
This obviously won't work how you wanted it to since it appears to be an issue with the Gradle plugin itself. If you just need to include the generate code in your project, then just include the generated Java code as part of your main Java source:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
But with this approach, you'll run into missing imports/dependencies. It doesn't appear this plugin offers the ability to just generate the models/POJOs only, so updating the library property to native and including some missing dependencies manually, it all works:
plugins {
java
id("org.openapi.generator") version "5.0.0-beta"
}
repositories {
mavenCentral()
}
group = "io.mateo.test"
dependencies {
implementation(platform("com.fasterxml.jackson:jackson-bom:2.11.1"))
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("org.openapitools:jackson-databind-nullable:0.2.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("io.swagger:swagger-core:1.6.2")
}
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
library.set("native")
configOptions.put("dateLibrary", "java8")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
You cannot configure it alike this, because build most certainly is an output directory, which would create a circular reference. Better try to add a new module and add that generator plugin into that module. If you can configure another module as outputDir, this could be referenced.
Even if the plugin resides in the root project, the destination needs to be a module.
The point is, that the root project always executes, opposite to module configutions.
I’ve just answered a very similar question. While my answer there is not perfect, I would personally still prefer the approach suggested there – and kind of repeated here:
Suggested Approach
I would keep the builds of the modules that depend on the generated API completely separate from the build that generates the API. The only connection between such builds should be a dependency declaration. That means, you’ll have to manually make sure to build the API generating project first and only build the dependent projects afterwards.
By default, this would mean to also publish the API module before the dependent projects can be built. An alternative to this default would be Gradle composite builds – for example, to allow you to test a newly generated API locally first before publishing it. However, before creating/running the composite build, you would have to manually run the API generating build each time that the OpenAPI document changes.
Example
Let’s say you have project A depending on the generated API. Its Gradle build would contain something like this:
dependencies {
implementation 'com.example:api:1.0'
}
Of course, the simple-java-app build described in the question would have to be adapted to produce a module with these coordinates:
openApiGenerate {
// …
groupId = "com.example"
id = "api"
version = "1.0"
}
Before running A’s build, you’d first have to run
./gradlew openApiGenerate from your simple-java-app project.
./gradlew publish from the simple-java-app/build/openapi-java-client/ directory.
Then A’s build could fetch the published dependency from the publishing repository.
Alternatively, you could drop step 2 locally and run A’s build with an additional Gradle CLI option:
./gradlew --include-build $path_to/simple-java-app/build/openapi-java-client/ …
I have seen this post Gradle multi project distribution but still have some doubts.
I would like to create the following project layout
root
|--lib-java-module
|--spring-boot-module
|--3PP_A_module # not java
| |-- custom scripts, config
|--3PP_B_module # not java
| |-- custom scripts, config
|--dist-module
As you might have guessed, I want the dist-module to build myapp-dist.tar.gz with libjava.jar, sprintbootapp.jar, 3pp-a.tar, 3pp-b.tar.
myapp-dist.tar.gz
libjava.jar
sprintbootapp.jar
3pp-a.tar
3pp-b.tar.
The 3pp-a-module and the 3pp-b-module only contain some configuration files and startup scripts. No java or any compiled code. How to package them individually into tar files (no compression)?
How to define dependencies in dist-module to the other modules? Is it possible to get the other modules built when build is triggered from dist-module?
Update:
I setup my test project based on #marco-r's answer and it works except for packaging the war file. Checkout the test project from github https://github.com/KiranMohan/study-spring-boot.
This is the project setup of interest.
include ':sb-2.1-multi-package', ':sb-2.1-multi-package:hello-rest-lib',
':sb-2.1-multi-package:hello-rest-standalone-jar',
':sb-2.1-multi-package:hello-rest-war'
include 'sb-2.1-3pp-resources'
include 'sb-2.1-build'
However adding hello-rest-war to sb-2.1-build.tar.gz fails.
Instead of war files, its the dependencies that are getting packaged.
dependencies {
archivesDeps project(path: ':sb-2.1-3pp-resources', configuration: 'archives')
javaDeps project(":sb-2.1-multi-package:hello-rest-war")
}
...
task copyJavaDeps(type: Copy) {
inputs.files(configurations.javaDeps)
from configurations.javaDeps
into "${ARCHIVE_DIRECTORY}/lib"
}
...
// create distribution bundle
distributions {
main {
contents {
from ARCHIVE_DIRECTORY
into "/springapp/multimodule"
}
}
}
Contents of the package
springapp/multimodule/lib/classmate-1.4.0.jar
springapp/multimodule/lib/hello-rest-lib-0.0.1-SNAPSHOT.jar
springapp/multimodule/lib/hibernate-validator-6.0.16.Final.jar
...
springapp/multimodule/lib/tomcat-embed-websocket-9.0.17.jar
springapp/multimodule/lib/validation-api-2.0.1.Final.jar
springapp/multimodule/sb-2.1-3pp-resources/config/3pp.json
How to package war file (hello-rest-war module) and without all the transitive dependencies?
This is multiple question scenario, so I am going to address it in parts.
Since all 3PP_X_module have the same building requirements create a build.gradle in each of the submodules that refer to an actual build gradle that have the common functionality required:
apply from: '../tarArtifact.gradle'
In the parent folder create the previously referred tarArtifact.gradle to have the functionality to TAR the contents of a subfolder (arbitrarily chosen as contents) of a referring subproject:
apply plugin: 'base'
task tarContents(type: Tar) {
from 'contents'
archiveName = "${project.name}.tar"
destinationDir file('build/tar')
}
artifacts {
archives file: tarContents.archivePath, type: 'tar', builtBy: tarContents
}
Since the archives configuration is wired to the output of the tarContents (builtBy: tarContents), then the archives configuration can be used to retrieve the desired TAR as the output of building this project naturally.
Create in dist-module the following build.gradle file:
apply plugin: 'distribution'
plugins.withType(DistributionPlugin) {
distTar {
compression = Compression.GZIP
extension = 'tar.gz'
}
}
configurations {
wholeProjectDist
}
dependencies {
wholeProjectDist project(path: ':3pp-a-module', configuration: 'archives')
wholeProjectDist project(path: ':3pp-b-module', configuration: 'archives')
wholeProjectDist project(':lib-java-module')
wholeProjectDist project(':spring-boot-module')
}
distributions {
main {
contents {
from configurations.wholeProjectDist
}
}
}
This gradle file includes the following:
Applies the Distribution plugin, so we can generate the final tar.gz file from the artifacts generated by all the other subprojects.
Configures the distTar task (of the DistributionPlugin plugin) to compress any generated TAR using it by using GZIP.
Creates the configuration wholeProjectDist to capture the dependencies of dist-module itself; which we will use with the distribution plugin's tasks.
Declares the dependencies of dist-module as the artifacts output by the siblings' subprojects; using the newly created wholeProjectDist.
Configures the distribution's plugin main configuration to have as contents all the files from configurations.wholeProjectDist
Create a settings.gradle file under dist-module to allow it to access its siblings modules using includeFlat:
includeFlat '3pp-a-module', '3pp-b-module', 'lib-java-module', 'spring-boot-module'
Include in the parent folder a settings.gradle file to include all children submodules (as the root project):
includeFlat '3pp-a-module', '3pp-b-module', 'lib-java-module', 'spring-boot-module'
Build the desired tar.gz files by invoking the gradle command (from the root folder):
gradle :dist-module:distTar
Hope this helps.
I have a library which is used to build a number of CLI tools using Gradle. Each CLI tool is a separate JAR. At the moment every tool requires a separate Gradle project, with an associated set of directories, like this:
Having all of this structure is resulting in the whole collection of tools becoming very unwieldy and difficult to work with. Is there any way to collect all of the different Mains into a single folder (suitably renamed) and configure Gradle to turn each one into a separate JAR?
FWIW, the JARs are currently created using https://github.com/johnrengelman/shadow . JAR size doesn't matter.
Thanks in advance.
Jars are just zip files with META-INF folder inside. Use Zip tasks to create them and dependsOn to run tasks as part of your build sequence.
I had the code like below for changing jar files:
task changeJar (type: Zip) {
baseName project.name
extension 'jar'
destinationDir new File('build')
entryCompression ZipEntryCompression.STORED
from { zipTree(new File(core.libsDir, core.name + '.jar')) }
from ( <somewhere else> ) {
exclude 'META-INF/'
}
}
I'm not sure if it's a good fit but you might be interested in my gradle-java-flavours plugin.
eg:
apply plugin: 'com.lazan.javaflavours'
javaFlavours {
flavour 'tool1'
flavour 'tool2'
}
dependencies {
compile 'a:a:1.0' // common to all tools
compileTool1 'b:b:2.0' // compile deps for tool1 only
runtimeTool2 'c:c:2.0' // runtime deps for tool2 only
}
Directories
src/main/java, src/test/java, src/main/resources, src/test/resources - common code & tests
src/tool1/java, src/testTool1/java, src/tool1/resources, src/testTool1/resources - tool1 only sources
src/tool2/java, src/testTool2/java, src/tool2/resources, src/testTool2/resources - tool2 only sources
Jars
projectName.jar
projectName-tool1.jar
projectName-tool2.jar
The big picture is that I have a protobuf (think of it like a class) datatype that refers to another protobuf that is within another Jar file that is a dependency in my POM file. This works perfectly for .java files but unfortunately doesn't work for protobuf files. The best solution I can think of is to tell Maven to extract this other dependency Jar (that contain proto files) file in a location and then tell Maven to do a protoc compile of all these proto files in that location. Alas, I dont know how to tell Maven to do this. Any help would be greatly appreciated as we use standard Jar files to capture our proto files.
import "X.proto"; // refers to a proto file in another jar
import "Y.proto";
message A {
repeated X o = 1; // This cant be done
repeated Y e = 2;
}
The above will not work since X is not in the same path as this file.
I found the solution for this in Gradle. Fill in the blanks below and point your repo correctly, and you should be able to get the below to work. Now you can hierarchically compose protobufs across multiple projects through proto files within other jar files !
In gradle, you can use the following:
// these are your protobuf plugin repositories
buildscript {
repositories {
maven {
url 'WHERE YOUR PROTOBUF PLUGIN EXISTS. e.g. http://maven or MavenCentral()'
}
}
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.7.0'
classpath 'com.google.protobuf:protoc:2.6.1'
}
}
apply plugin: 'com.google.protobuf'
group = "YOUR PACKAGE NAME. e.g. com.x.y"
version = "1.0" // release version
// these are your regular dependencies repositories. This could be
// very similar to the ones in buildscript above.
repositories {
mavenLocal()
}
// this is needed by the plugin and is where teh magic happens. It
// tells protobuf where to extract all the proto files that it finds
// in all the dependency jar files you have
protobuf {
generatedFilesBaseDir="$projectDir/src/"
}
// also magic you need for this protobuf plugin to work
sourceSets {
main {
// this tells the plugin where your project specific
// protofiles are located
proto {
srcDir 'src/main/resources/proto/'
}
java {
srcDir 'src/main/java'
}
}
}
dependencies {
compile 'com.google.protobuf:protobuf-java:2.6.1'
}
I have an EAR project which should contain one or more skinny WARs. I already tried everything to get that project working with eclipse but just couldn't make eclipse do the same as the tools(maven and gradle) do when I run them from the command line.
Are there no working examples I could use to get my projects working with eclipse? Please help me, I alread ask myself if anyone is really using these tools like I want them to for such kind of projects.
In my last project experience I have problem with supporting the maven with Eclipse. Because of problem in Eclipse m2 plugin.
So the best solution for me was build an ear from the command line by some shell scripts for example. To open project in IDE I used maven eclipse plugin, thus I generated eclipse workspace by maven.
Using Eclipse External Tools you can run shell script to build/or run your EE application from the command line pretty convenient.
The same applies to the gradle, but looks like Eclipse Gradle plugin is more stable, and now I use plugin in my Gradle project.
If it will be useful for you, you can review github test project to illustrate how to make maven multymodule war project. Also you can find short explanation how to generate eclipse workspace for this project. After workspace generated you can import as Existing Project into your workspace.
Here is a sample Ear project containing war(Refer to img below for dir structure)
MainDir contain 2 files and 1 directory called war.
File settings.gradle contains
include 'war'
File build.gradle contains
apply plugin: 'ear'
repositories {
mavenCentral()
}
dependencies {
deploy project(':war')
//earlib group: YOUR_DEPENDENCIES
}
build.gradle for war directory contains
apply plugin: 'war'
apply plugin: 'jetty' // you can call gradle jRW
repositories {
mavenCentral()
}
dependencies {
//compile group: YOUR_DEPENDENCIES
}
httpPort = 8080 //jetty start port
stopPort = 8081 //jetty stop port
File HelloWorld.java contains
public class HelloWorld {
public String getHello() {
return "Hello world!";
}
}
File index.jsp contains
<jsp:useBean id="helloWorld" class="your_package.HelloWorld"/>
<html>
<p>${helloWorld.hello}</p>
</html>
Now open cmd->MainDir(or you can search eclipse-marketplace for gradle and execute this step directly from eclipse) and type
gradle jRW//short for jettyRunWar
now open
localhost:8080/war/