Two JARs with one set of sources in Gradle - java

Suppose I have one set of Java sources in the standard repository layout (ie. src/main/java). Now suppose I want to create two jars from this set of sources - one each using the classes produced from two different JavaCompile tasks that are configured differently. For example, I might have something like this:
tasks.named('compileA', JavaCompile).configure {
// one set of toolchain and options settings here
}
tasks.named('compileB', JavaCompile).configure {
// a different set of toolchain and options settings here
}
The question is, is it better to just have the main source set (supplying the compileA equivalent, and thus the jarA equivalent, for free) and commission compileB (and jarB) separately, or have two separate source sets, one each for compileA/jarA and compileB/jarB? What if I needed something like a war, where I need the source set's runtime classpath? Is there a way that I can make a warA and warB, where they each only have the classes from one or the other compile task?
Additionally, suppose that I have a multi-project build, where multiple projects have this compileA/compileB setup, and there are project dependencies between the subprojects. What else would I need to do in order to make it so that we end up with a jar that only consists of classes compiled using the various compileA tasks, and a different jar that only consists of classes compiled using the various compileB tasks?

Related

How class loader works where jvm has jars having same names but different versions, which one would be loaded at runtime or both will?

There are two jars which have the same name which is being used in the application but for different purposes.
The name of the jar for example is "A1.jar" having different versions and used in if else condition.
if this:
then A1-10.2.3jar.create()
else:
then A1-8.18.0jar.create()
which in turn have different implementations of create method.
The question is when the application is loaded which jar will be instantiated during class loading time? Or depending upon condition the corresponding jar will be loaded? How can we ensure the correct jar is picked up each time.
The question is further transported to application server scenario where both libs are in WEB-INF/lib folder on jboss/wildfly. How would classloader behave there?
This happened in one of the cases for wildfly where had same name of jars but it was picking up the wrong one and creating an issue. But the same case was running fine on another environment..is there any order/precedence for this?
Update:
The classes are different when it is being called:
Say A2 and A3 class which then calls the A1 jar which are coming as a dependency from class A2 and A3 with same names but different versions.
So, this will be the case:
if this:
then A2.create()-> calls A1-10.2.3jar.respone()
else:
then A3.create()->calls A1-8.18.0jar.respone()
If this is the case can classloader load both the classes or it can be random?
As far as i know, there is no specification for jee application server which version they have to use in this scenario. Therefore it's kind of (deterministic) random, which jar is used.
This really should be avoided, for example with an exclude for the deployment in a maven file or something similar for gradle.

PsiClass to java.lang.Class

I'm developing plugin for IntelliJ IDEA. How can plugin get the name and version of libraries that are imported to the project that is being checked by plugin? I have PsiClass of the project, but cannot convert it to java.lang.Class. Maybe there's the way to get ClassLoader from PsiElement?
super.visitImportStatement(psiImport);
Class importedClass = Class.forName(psiImport.getQualifiedName(), true, psiImport.getClass().getClassLoader());
PsiImport.getClass().GetClassLoader() - returns ClassLoader of class PsiImportStatementImpl instead of ClassLoader of class that I've imported.
IntelliJ does mostly static analysis on your code. In fact, the IDE and the projects you run/debug have completely different classpaths. When you open a project, your dependencies are not added to the IDE classpath. Instead, the IDE will index the JARs, meaning it will automatically discover all the declarations (classes, methods, interfaces etc) and save them for later in a cache.
When you write code in your editor, the static analysis tool will leverage the contents of this index to validate your code and show errors when you're trying to use unknown definitions for example.
On the other hand, when you run a Main class from your project, it will spawn a new java process that has its own classpath. This classpath will likely contain every dependency declared in your module.
Knowing this, you should now understand why you can't "transform" a PsiClass to a corresponding Class.
Back to your original question:
How can plugin get the name and version of libraries that are imported to the project that is being checked by plugin?
You don't need to access Class objects for this. Instead, you can use IntelliJ SDK libraries. Here's an example:
Module mod = ModuleUtil.findModuleForFile(virtualFile,myProject);
ModuleRootManager.getInstance(mod).orderEntries().forEachLibrary(library -> {
// do your thing here with `library`
return true;
});

Android Annotation Processor accessing Annotated classes from different modules

I'm having an Android Studio project with 2 modules: A and B. (I do not include here the Annotation Processor and the Annotations module)
B depends on A.
B is an Android Library Module, and A is simple Java library Module. I'm also having an Annotation Processor on module B.
The problem I'm facing is:
I want to generate some code, based on annotated files placed in both modules - A and B. The problem comes from the way the Annotation Processor works - only with source code files *.java - not with compiled *.class ones. Unfortunately, during the compilation of B, the Annotation Processor doesn't have access to those source files from A...
The only thing, I was able to think about as a kind of solution, even an ugly one, was to include the folder with the annotated classes from module A as a source set to module B. This way I give module B access to those files during compilation.
sourceSets {
main {
java {
srcDirs = ['src/main/java', '../module_A/src/main/java/path/to/annotated/classes/folder']
}
}
}
That solves the problem - now the Annotation Processor has access to all the annotated classes from both modules, but...
Unfortunately, it introduces another issue... those annotated classes from module A, are now compiled twice. And they are included in the module A's JAR file and in the module B's AAR file.
Question 1: Is there another way to access those source files of module A, from the Annotation Processor running on B??? (From what I was able to find, the answer is NO, but checking...)
Question 2: How can I exclude those compiled files (the repeated ones) from the AAR final package of module B?
Question 3: Maybe... that's an absolutely wrong approach? Any suggestions?
Thanks in advance!
Nop, you can not achieve what you want using just java.lang.model API. At least not without some additional tricks.
The issues is not with binary-vs-source. Annotation processors can use Elements#getTypeElement to interospect compiled classes as well as source-defined classes:
Elements elementUtil = processingEnvironment.getElementUtils();
TypeElement integerClass = elementUtil.getTypeElement("java.lang.Integer");
TypeElement myClass = elementUtil.getTypeElement("currently.compiled.Class");
But you still need to have class on compilation classpath to observe it, and the class must be in process of being compiled to be visible to getElementsAnnotatedWith.
You can work around later limitation by using a tool like FastClasspathScanner: it will use it's own mechanisms to find annotations in compiled bytecode, and report them to you separately from compilation process. But you can not work around the classpath issue: if you don't have some dependency in compilation classpath, it can not be processed. So you have to compile modules together — either by merging them into one (as you did) or via declaring one to depend on another. In later case you might not be able to use getElementsAnnotatedWith, but getTypeElement and FastClasspathScanner will work.

How to depend on project with WebApplicationInitializer?

I have a project structure, where I have some basic projects, where all the features are implemented and on top of that some projects for different kinds of usage/deployment as follows:
foo-core
foo-production, depends on foo-core (produces a war file and i delivered to customers)
foo-standalone, depends on foo-core (produces a war used for internal testing)
foo-ui-test, depends on foo-core (UI tests with Arquillian+Selenium)
Now I have a rising number of classes that are useful for foo-standalone and for foo-ui-test, but I don't want to deploy these classes to the customer. Therefore foo-core is out as a possible project where to store these classes. The two remaining options are:
A new project foo-helper which depends on foo-core and make foo-standalone and foo-ui-test depend on foo-helper instead of foo-core.
Make foo-ui-test depend on foo-standalone
I'd like to go for option 2 as I already have a lot of projects. The remaining problem for option 2 is that both of these projects (foo-standalone and foo-ui-test) have a Spring WebApplicationInitializer...
Now I fiddled around with my build system (I use gradle) and was able to exclude the WebApplicationInitializer in the foo-standalone project and only explicitly include it when building the foo-standalone.war file.
The resulting build.gradle file for foo-standalone seems "hackish" and will break if I rename or move the WebApplicationInitializer class. I wonder, if there is another way to achieve what I'm looking for or if I have to make a new project.
Update
I moved the WebApplicationInitializer class from src/main/java to src/init/java and changed the build.gradle of foo-standalone as follows:
sourceSets {
init {
java {
compileClasspath += sourceSets.main.runtimeClasspath
}
}
}
war {
classpath sourceSets.init.runtimeClasspath
}
I guess that's the shortest, custom way to do this, or is there any built-in gradle functionality which does this?
Maven will only allow you to have a dependency to a JAR project. There is a feature where you can have war overlays, where one war project extends a child and overrides certain bits (see https://maven.apache.org/plugins/maven-war-plugin/overlays.html) this seems the relationship between your foo-standalone and foo-ui-test.
So make foo-helper a war project and make foo-standalone, foo-ui-test and foo-production extend it using war overlays.
foo-core can stay as a jar, if there are non-production bits create a foo-core-internal to depend on foo-core and ring fences the internal/test bits.

Conditional Java compilation

I'm a longtime C++ programmer, new to Java. I'm developing a Java Blackberry project in Eclipse. Question - is there a way to introduce different configuration sets within the project and then compile slightly different code based on those?
In Visual Studio, we have project configurations and #ifdef; I know there's no #ifdef in Java, but maybe something on file level?
You can set up 'final' fields and ifs to get the compiler to optimize the compiled byte-codes.
...
public static final boolean myFinalVar=false;
...
if (myFinalVar) {
do something ....
....
}
If 'myFinalVar' is false when the code is compiled the 'do something....' bit will be missed out of the compiled class. If you have more than one condition - this can be tidied up a bit: shift them all to another class (say 'Config.myFinalVar') and then the conditions can all be kept in one neat place.
This mechanism is described in 'Hardcore Java'.
[Actually I think this is the same mechanism as the "poor man's ifdef" posted earlier.]
you can manage different classpath, for example, implement each 'Action' in a set of distinct directories:
dir1/Main.java
dir2/Action.java
dir3/Action.java
then use a different classpath for each version
javac -sourcepath dir1 -cp dir2 dir1/Main.java
or
javac -sourcepath dir1 -cp dir3 dir1/Main.java
In JDK6, you can do it by using Java's ServiceLoader interface.
Check it here.
If you want this specifically for BlackBerry, the BlackBerry JDE has a pre-processor:
You
can enable preprocessing for your
applications by updating the Eclipse™
configuration file.
In C:\Program Files\Eclipse\configuration\config.ini,
add the following line:
osgi.framework.extensions=net.rim.eide.preprocessing.hook
If you enable preprocessing after you
have had a build, you must clean the
project from the Project menu before
you build the project again.
Then you can do things in the code like:
//#ifdef SOMETHING
// do something here
//#else
// do something else
//#endif
For details see Specifying preprocessor defines
Can one call that a poor mans ifdef: http://www.javapractices.com/topic/TopicAction.do?Id=64?
No, Java doesn't have an exact match for that functionality. You could use aspects, or use an IOC container to inject different implementation classes.
You can integrate m4 into your build process to effectively strap an analogue to the C preprocessor in front of the Java compiler. Much hand-waving lies in the "integrate" step, but m4 is the right technology for the text processing job.
Besides Maven, Ant and other build tools that provide similar functionality, one would rather build interfaces in Java and switch the implementations at Runtime.
See the Strategy Pattern for more details
In opposite to C/C++ this will not come with a big performance penality, as Javas JIT-compiler optimizes at runtime and is able to inline this patterns in most cases.
The big pro of this pattern is the flexibility - you can change the underlying Implementation without touching the core classes.
You should also check IoC and the Observer Pattern for more details.
You could use maven's resource filtering in combination mit public static final fields, which will be indeed get compiled conditionally.
private static final int MODE = ${mode};
...
if (MODE == ANDROID) {
//android specific code here
} else {
}
Now you need to add a property to your maven pom called "mode", which should be
of the same value as your ANDROID constant.
The java compiler should (!) remove the if and the else block, thus leaving your android code.
Not testet, so there is no guarantee and i would prefer configuration instead of conditional compilation.
There are a couple of projects that bring support for comment-based conditional compilation to Java:
java-comment-preprocessor
JPSG
Example in JPSG:
/* with Android|Iphone platform */
class AndroidFoo {
void bar() {
/* if Android platform */
doSomething();
/* elif Iphone platform */
doSomethingElse();
/* endif */
}
}
In eclipse you could use multiple projects
Main (contains common code)
Version1 (contains version1 code)
Version2 (contains version2 code)
Main -> Select Project->Properties->Java Build Path->Projects tab
Select Add...
Add "Version1" xor "Version2" and OK back to the workspace.
Version1 and Version two contain the same files but different implementations. In Main you normally write e.g.
import org.mycustom.Version;
And if you included Version1/Version2 project as reference it will compile with the Version.java file from Version1/Version2 project.

Categories

Resources