How to write AOP #Around for a package from specific dependency - spring

I have multiple packages with same name like org.abc.com.client.* in different external libraries/dependencies which have same groupId name but unique artifactId name.
Therefore, it looks like:
<dependency>
<groupId>same_name</groupId>
<artifactId>unique</artifactId>
<version>same_or_different</version>
</dependency>
and unique artifactId has same package name. I don't need AOP for a class.
I want write #Around for a package specific to particular artifactId.
#Around("call(* package(org.abc.com.client)_of_specific_artifactId1(..))")
public void doSomething(){}
#Around("call(* package(org.abc.com.client)_of_specific_artifactId2(..))")
public void logSomething(){}

The artifact ID is a piece of build-time information and should be utterly irrelevant during runtime. BTW, artifact IDs can change, dependencies be re-packaged into uber JARs via Maven Shade or Maven Assembly plugins etc. What you want to do is simply bad design. You should refactor your libraries or find another way to differentiate them from each other.
FWIW, you can solve your problem also during build time by applying native AspectJ aspects to selected artifacts using binary weaving, creating new woven artifacts which can replace the original ones. The AspectJ Maven plugin does that for you, if configured correctly. For Gradle have a look at Freefair.
Update: About 7 years ago, I answered another question involving binary weaving of input JARs. It is a more complex multi-module scenario and if you read the whole answer, you are also going to find a link to a GitHub repository with a complete MCVE. Just replace the the Maven plugin org.codehaus.mojo:aspectj-maven-plugin:1.7 by dev.aspectj:aspectj-maven-plugin:1.13, because the MojoHaus version only supports up to Java 8 and misses some features compared the Aspectj.dev version.

Related

How to manually add dependencies to Gradle's MavenPom/MavenPublication?

I am working on a plugin. This plugin gets attached to a project that does not apply the java plugin nor the java-library plugin but which should functionally "look" like a Java project[1]. Which means that it should publish a POM including dependencies. The exact dependencies are known and have been collected in a Configuration.
However, I cannot figure out how to manually attach dependencies to the MavenPublication such that they make it into the published pom (aside from directly editing the pom xml).
MavenPublication shadowMavenPublication = publishingExtension.getPublications().create( "mavenShadowArtifacts", MavenPublication.class );
// `shadowPublishArtifact` is a class defined in the plugin
shadowMavenPublication.artifact(
shadowPublishArtifact.getFile(),
(mavenArtifact) -> {
mavenArtifact.setClassifier( shadowPublishArtifact.getClassifier() );
mavenArtifact.setExtension( shadowPublishArtifact.getExtension() );
}
);
So at this point I have the MavenPublication and added my custom artifact to it. Internally this MavenPublication contains a number of "dependencies" as instances of MavenDependency. E.g. DefaultMavenPublication#runtimeDependencies, DefaultMavenPublication#apiDependencies, ... But those are things defined on internal-only contracts.
Using just public APIs, how can I add dependencies to get added to the pom?
P.S. As a bonus, answer the question on the Gradle forums and get points there too! :D
P.S.S. These dependencies come from another project (hibernate-core) in a multi-project build. The user has configured those dependencies themselves. I just "consume" those dependencies with a series of "dependency substitutions". That "source project" defines some exclusions to its dependencies. How can I access those exclusions do be able to transfer them to the dependencies I am creating for this copy project (hibernate-core-jakarta)?
Thanks!
[1] Its a long back-story, but the gist is that this plugin integrates the JakartaTransformer. The project is completely generated using the transformer. The tasks added by those 2 plugins cause problems.
MavenPublication class has pom property - You need to construct (or provide in Your plugin some API for that purpose) pom with all necessary dependencies. It will be published alongside with artifact.
As far as I know, dependencies are attached to the POM by evaluating the configurations of a software component: MavenPublication.from(SoftwareComponent) (source: DefaultMavenPublication).
The idea would be to provide a customized software component. This is only possible through a custom plugin, according to Creating and publishing custom components.

What does it really mean that api configuration exposes depedencies whereas implementation does not, in Gradle?

I have gone through the official doc and many StackOverflow questions about the difference betweenapi and implementation configurations. I think I understand the basic, but I really want to understand what it means by dependencies are exposed or not exposed.
This is what I have got so far. When I publish my Java library (written in Kotlin, but not relevant), the dependency scope in the published pom file is either complie when api is used or runtime when implementation is used, i.e.
dependencies {
api "..."
}
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<scope>compile</scope>
</dependency>
dependencies {
implementation "..."
}
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<scope>runtime</scope>
</dependency>
So does exposing dependencies in this case really just mean adding them to classpath (compile scope)?
One of the many answers about api vs implementation says it is merely about build optimization, it makes sense that the build time will be reduced if we not adding everything in the classpath maybe?
And a bonus question, the Gradle doc says api configuration comes with java-library plugin, but apparently, I can use it without applying the plugin, how is this possible?
// Gradle 6.1.1
plugins {
id 'org.jetbrains.kotlin.jvm' version 'XXX'
}
dependencies {
api "myLibrary"
}
So does exposing dependencies in this case really just mean adding them to classpath (compile scope)?
Yes, it's pretty much just a matter of having them on the consumer's compile classpath or not.
One of the many answers about api vs implementation says it is merely about build optimization, it makes sense that the build time will be reduced if we not adding everything in the classpath maybe?
Well, good software design advocates not exposing internal implementation details. This is why you have public and private class members in the code. You could argue that this principal is solid when it comes to dependencies as well. I see the following benefits:
A consumer does not implicitly start relying on "internal" transitive dependencies. If they did, it would mean that you can't remove them from the library without breaking the consumers.
A reduced classpath may make compilation slightly faster. I don't think it matters a whole lot for normal projects though. Maybe it is more impactful if you rely on Java or Kotlin annotation processors or Groovy AST transformations that feels like scanning the entire classpath through each time.
Not having unnecessary modules on the compilation classpath means a library will not have to be recompiled if those modules changes.
The last one is the biggest benefit in my opinion. Let's say you have a big multi-project where a shared sub-project internally relies on Apache Commons Lang. If you have declared Lang as an api dependency and update it, then all other projects relying on this shared project need to be recompiled. If you declare it as an implementation dependency instead, this will not happen. All those projects will still need to be re-tested of cause as the runtime behaviour might have changed (this is handled correctly by default in Gradle).
And a bonus question, the Gradle doc says api configuration comes with java-library plugin, but apparently, I can use it without applying the plugin, how is this possible?
This is because the Kotlin plugin also declares an api configuration. It has the same semantics as configured by the java-library plugin.
If your project is a multi-project, you can still add the java-library plugin even if it is using the Kotlin plugin. An additional change that this will cause is that consumers will see the output directory for the compiled classes instead of the final jar file. This removes the need to construct the jar during normal development, which should reduce build time. On the other hand, there is apparently a potential performance problem on Windows if you have a lot of classes in a single project, so the usual your mileage may vary disclaimer applies here as well (I don't know how many "a lot" is though).

Dealing with other dependencies in your own Maven dependency

I want to reuse and centralize the utils I created for my Spring REST API for my future projects. That's why I thought I'd outsource them to my own project and make them available as a Maven dependency.
These Util files e.g. a basic service, basic controllers also contain Spring annotations, i.e. I need some Spring dependencies in my Util dependency. Now I'm a bit unsure whether I'm making a mistake or not.
First of all, I'm not sure if I should even use spring dependencies in a utility dependency or try to remove everything. Otherwise, I'll have to specify a spring version, but it might differ from the version I want to use later in the project it's included in. How am I supposed to solve this?
It is perfectly reasonable to have dependencies for your dependencies (these are called transitive dependencies). Of course, you should keep the number as low as possible, but on the other hand, you do not want to reinvent the wheel.
When somebody uses your dependency, they will automatically draw the transitive dependency on spring. Now, several cases can occur:
If this is the only reference to spring, the version is just used as you stated it.
If at some other point, a different version of spring is given, Maven dependency mediation kicks in. It decides by a "nearest is best" rule which version to take.
But: You can always set the spring version in <dependencyManagement> and then overwrite all transitively given version numbers.
That is the main concept of Maven. Your utility module must shipped together with Spring dependencies. It's called transitive dependencies.
Try to imagine that situation when all dependencies had excluded. In that case nobody will never know what kind and which version of Spring dependencies are needed.
Maven has a very good dependency conflict resolution. It's based on nearest-newest principle. So you can override those Spring versions easily and your application will use only one of that.
Take a look at these:
[1] Dependency Mechanism
[2] Dependency Mediation and Conflict Resolution

Maven: xpp3 versus xpp3_min

In my Java Maven project, two of my codes direct dependencies use a sub-dependency of XPP3. However, one of them has the artifact ID xpp3 while the other one has the artifact ID xpp3_min. Both are version 1.1.4c. Does anyone know the difference between the two? My project allows both to be dependencies without marking either of them as excluded due to conflict.
Home page for XPP3 project: http://www.extreme.indiana.edu/xgws/xsoap/xpp/
Maven repository reference: http://mvnrepository.com/artifact/xpp3/xpp3_min and http://mvnrepository.com/artifact/xpp3/xpp3. Notice how both projects have the same description. I don't just want to naively assume that _min is a minimal version due to its name suffix.
I've opened both archives and the xpp3_min only includes the XmlPullParser.class and XmlPullParserException.class (and MXParser.class). It doesn't include other classes like XmlPullParserFactory etc...
A popular obj <-> xml serialzer package: XStream, has both a dependency on XPP3_MIN and XMLPULL, where XMLPULL implements the XmlPullParserFactory. If it had a dependency on XPP3 it sure would have a classloading issue.

Maven dependencies - how to add dependencies based on classpath properties

Is it not possible to include dependencies based on class properties? E.g. if I am building a framework that I want to integrate with any customer system, the type of DB the customer uses could be a variable but my framework may use it if it can acquire a data source. So in this case, my Maven project should be able to integrate with any DB by declaring the corresponding DB war as dependency.
E.g.
<dependency>
<artifactId>${database.artifactId}</artifactId>
....
</dependency>
But this database.artifactId in itself will be read from properties file that may be accesible to customer code, so the idea of having parent pom declare the versions and artifactId as mentioned here may not suit my case.
Is there a work around or is this use case itself so wrong? I strongly think if we build a framework that is more like a product the customer can integrate with, this flexibility of declaring any runtime dependency based on propertie should be there.
Thanks,
Paddy
This is not how it is done with Maven.
If, as in your case, you write a framework that may use different dependencies, then you do not somehow conditionally depend on the concrete implementation. This would not work, as the exact list of dependencies is constructed at build time (i.e. when the Maven artifact of you framework is built and installed).
Rather, there should be a special Maven artifact which describes just the interface of the functionality that you need. This artifact will typically contain the (Java) interfaces your framework needs; this is what your framework will depend on.
Then, when using your framework, a concrete implementation of these interfaces must be included - by the people using your framework, because only they know which implementation they use.
This is explained for example in "Maven by Example", chapter "7.10.1. Programming to Interface Projects".
Example:
JDBC: The interface is part of the JDK, so a framework that uses JDBC does not need to declare any special dependencies (if JDBC were not part of the JDK, you would depend on a Maven artifact like "jdbc-api" or similar). A software that actually uses JDBC will have to depend on whatever JDBC driver it actually uses (Oracle, HSQL etc.).

Resources