Load Maven Artifact via Classloader - java

Is it possible to load remote artifacts via Maven during runtime, e.g. using a specific (Maven) ClassLoader?
For my use case, a legacy software is using an URLClassLoader to pull a JAR containing some resources files during start-up of a test framework.
Problem is that we currently just use a fixed URL pointing to the repository and not actually using Maven artifact resolution at all.
Adding this to the projects dependency is no option because we want to refer to a specific version from an external configuration file (to run the test framework with different versions of our packaged use cases without changing code).
I hope you get what I want to achieve - it doesn't have to be the prettiest solution because we currently rely on a fixed URL pattern, I'd like to be dependent from the local maven setup instead.

You may use Eclipse Aether (http://www.eclipse.org/aether) to resolve and download the JAR artifacts from maven repositories using GAV coordinates.
Then use a regular URLClassLoader with the JAR you've downloaded.
You can find some examples there: https://github.com/eclipse/aether-demo/blob/master/aether-demo-snippets/
But basically, what you should do is the following:
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
RepositorySystem system = locator.getService(RepositorySystem.class);
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
LocalRepository localRepo = new LocalRepository("/path/to/your/local/repo");
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
// Set the coordinates of the artifact to download
Artifact artifact = new DefaultArtifact("<groupId>", "<artifactId>", "jar", "<version>");
ArtifactRequest artifactRequest = new ArtifactRequest();
artifactRequest.setArtifact(artifact);
// Search in central repo
artifactRequest.addRepository(new RemoteRepository.Builder("central", "default", "http://repo1.maven.org/maven2/").build());
// Also search in your custom repo
artifactRequest.addRepository(new RemoteRepository.Builder("your-repository", "default", "http://your.repository.url/").build());
// Actually resolve (and download if necessary) the artifact
ArtifactResult artifactResult = system.resolveArtifact(session, artifactRequest);
artifact = artifactResult.getArtifact();
// Create a classloader with the downloaded artifact.
ClassLoader classLoader = new URLClassLoader(new URL[] { artifact.getFile().toURI().toURL() });

Related

Maven Plugin: Manipulate resources during packaging

in my maven project, I've got a xml file in resources. Depending on some input parameter I want the file to be adapted before packaged into a jar or war. Of course, the original file shall not be touched.
It is not an option to create multiple xml-files and select a suitable one, for example, with spring profiles as there can be numerous combinations of contents in the xml file.
So, I thought of creating a maven plugin, that manipulates the file before packaging. Probably, I need to manipulate the file, when maven has copied the file to the target folder but before maven packages the file into the jar/war.
#Mojo(name = "manipulate-xml", defaultPhase = LifecyclePhase.PREPARE_PACKAGE)
public class MyMojo extends AbstractMojo {
#Parameter(defaultValue = "${project}", required = true, readonly = true)
MavenProject project;
#Parameter(property = "option")
String option;
public void execute() throws MojoExecutionException {
if (option.equals("optionA")) {
// get file from target and manipulate
} else if (option.equals("optionB")) {
// get file from target and manipulate
}
}
}
Then, I could embedded the maven plugin into my project and build the project with
mvn clean package -Doption=optionA
However, now I am stuck. I do not know, how to get the file from target and even if this is the right approach.
Besides, is it possible during the packaging to prevent some dependencies from being packaged into the jar/war?
I appreciate any help.
Depending on what manipulating means, you can use the possibilities of the maven resources plugin (https://maven.apache.org/plugins/maven-resources-plugin/index.html).
If you need to modify some simple values inside the xml, use properties in the xml and let the resources plugin replace them during build. The values for the build can be either in the pom.xml or given to maven via -Dproperty=value.
If you want to select a different files, define multiple maven profiles, in each you can configure the resources plugin to copy only the wanted files and then select the correct profile in the build.
If the built-in possibilities are not enough, you might even program your own filter for the resources plugin, that might be easier than writing a custom full fledged maven plugin.

Zeppelin does not see dependencies from custom repository

I want to add company artifactory to Zeppelin spark interpreter and try to use this document.
So, the URL of our artifactory looks like
http://artifactory.thecompany.com:8081/artifactory/
The access is not restricted to specific user and artifacts are downloadable both from my machine and from machine where Zepplin is running (I tried this with curl).
I've copied the artifact ID from by build.gradle, so I am pretty sure it is correct. However when I try to add the artifact that should be found in my company's artifactory I get error
Error setting properties for interpreter 'spark.spark': Could not find
artifact
com.feedvisor.dataplatform:data-platform-schema-scala:jar:3.0.19-SNAPSHOT
in central (http://repo1.maven.org/maven2/)
This error message sounds like Zeppelin did not try to look for my dependency in custom repository.
I tried to play with artifactory URL using:
http://artifactory.thecompany.com:8081/artifactory/
http://artifactory.thecompany.com:8081/
as well as with "snapshot" property of "Add New Repository" form (using true and false) but nothing helped. The error message does not disappear and classes from the referenced artifact are not found.
Thanks in advance.
For Zeppelin to use your company's repo by default you can set ZEPPELIN_INTERPRETER_DEP_MVNREPO in your ${Z_HOME}/conf/zeppelin-env.sh:
export ZEPPELIN_INTERPRETER_DEP_MVNREPO=http://artifactory.thecompany.com:8081/artifactory/
Alternatively, you can use Dynamic Dependency Loading feature of the notebook:
%dep
z.reset()
z.addRepo("Artifactory").url("http://artifactory.thecompany.com:8081/artifactory/").snapshot()
z.load("com.feedvisor.dataplatform:data-platform-schema-scala:3.0.19-SNAPSHOT")

gradle: downloading specific file from ivy repository

We use an internal ivy repository and are in the process from moving away from ant / ivy and tasking everything in gradle. I have my ivy repository set up in gradle as so:
repositories {
ivy {
url "${ivy_repository_url}"
layout "pattern", {
ivy "repository/[organisation]/[module]/[revision]/[artifact].[ext]"
artifact "repository/[organisation]/[module]/[revision]/[artifact].[ext]"
m2compatible = true
}
credentials {
username "${ivy_repository_username}"
password "${ivy_repository_password}"
}
}
}
and upon executing a task it resolves as expected, however, in some instances it pulls down everything in the repository if the naming convention doesn't match so I end up with a lot of extra stuff, javadocs.zip, sources.zip and everything else.
To get around this I want to download the specific jar files to a temp folder first and then compile them from local but I have no clue how to tell gradle to download a file that is named differently from the module name.
Example:
ivyFiles 'net.sourceforge.jtidy:jtidy:r938#jar'
downloads just jtidy-r938.jar from the net.sourceforge.jtidy repository but something like
ivyFiles 'com.gargoylesoftware:htmlunit:2.7#jar'
would pull htmlunit-2.7.jar but not the file htmlunit-core-js-2.7.jar.
If I omit the #jar it reverts to calling the ivy.xml file and I am left with all the junk + dependencies which I am trying to avoid. I have tried the following with no success
ivyFiles ('com.gargoylesoftware:htmlunit:2.7'){
artifact{
name = 'htmlunit-core-js'
type = 'jar'
}
}
There must be a way to do this.
Thank you
I figured it out. This is what I ended up doing
ivyFiles ('com.gargoylesoftware:htmlunit:2.7'){
transitive = false
artifact {
name = 'htmlunit-core-js'
extension = 'jar'
type = 'jar'
}
}
Now only the htmlunit-core-js-2.7.jar file was downloaded

Get all the dependencies of a MavenProject (including transitive ones) using Aether

How can you get all the dependencies of a MavenProject (including transitive ones) using Aether?
I have seen numerous examples where you specify the gav and it resolves the artifact and all it's dependencies. This is all fine. However, if your plugin is supposed to be invoked from the same project whose dependencies you're trying to resolve, this does not seem to work (or perhaps I am doing it wrong). Could somebody please give me a working example of how to do it?
I have tried the example with jcabi-aether shown in this SO post.
Try to use an utility class Classpath from jcabi-aether:
Collection<File> jars = new Classpath(
this.getProject(),
new File(this.session.getLocalRepository().getBasedir()),
"test" // the scope you're interested in
);
You will get a list of JARs and directories which are in "test" scope in the current Maven project your plugin is in.
If you're interested to get a list of Artifacts instead of Files, use Aether class directly:
Aether aether = new Aether(this.getProject(), repo);
Set<Artifact> artifacts = new HashSet<Artifact>();
for (Artifact dep : this.getProject().getDependencyArtifacts()) {
artifacts.addAll(aether.resolve(dep, JavaScopes.COMPILE));
}

Retrieving Maven Artifact from Repository using Maven Java API

If I have a Maven Artifact information (GroupId, ArtifactId, Version) how can I programmatically (using Java) retrieve that Artifact from my local repository?
Specifically, I need to be able to connect to the Maven Repository and create/retrieve a org.apache.maven.artifact.Artifact so I can retrieve the file associated with the Artifact.
I have looked into m2e source code, but the MavenImpl.java (which provides Artifact resolution) is way more complex than what I need and it is difficult to understand how the connection to the repository works.
You'll probably want to look at Aether. See the Wiki for examples.
You can construct a URL from the given information and download the file (note, replace the '.' in the <groupId> with '/'):
<repositoryUrl>/<groupId>/<artifactId>/<version>/<artifactId>-<version>.<type>
This is how we do it in jcabi-aether:
final File repo = this.session.getLocalRepository().getBasedir();
final Collection<Artifact> deps = new Aether(this.getProject(), repo).resolve(
new DefaultArtifact("junit", "junit-dep", "", "jar", "4.10"),
JavaScopes.RUNTIME
);
Give it a list of remote repositories, a location of a local repo, and Maven coordinates of the artifact. As the name shows, the library uses Apache Aether from Sonatype.

Categories