Can an annotation processor make a new java module? - java

I am working on a tool for building modular projects, and one potential api for declaring external dependencies is for users to declare them directly on their module-info.java(s)
#Dependency(group="org.apache.commons", artifact="commons-lang3", version="3.12.0")
module jproject {
requires java.base;
requires jproject.annotations;
// Should come from the dependency above...somehow
requires org.apache.commons.lang3;
}
Bootstrap problem of getting jproject.annotations on the module-path aside, I suspect but have not yet confirmed that I could do the dependency resolution work here with an annotation processor.
javac --module-source-path modules/
--module jproject
--processor-path download_deps.jar
-proc:only
-d target/modules/
javac --module-source-path modules/
--module jproject
--processor-path ...
-d target/modules/
What I can't figure is if its even conceptually possible to merge these two steps - calling javac and having a processor download the relevant files too a common place.
Looking at the Filer class available to processors I see createResource which feels like the right direction to dump downloaded jars that way, but I don't understand the compilation process enough to know if the filer's output would go on the module-path and if not if there is any way to do this.

Related

Compile non-modular code that depends on modules

I have a folder with two compiled modules simple-modules/module1 and simple-modules/module2 these contained exploded code, so not its jar and both modules contain a module-info.java
I have other non-modular source code in folder normal-java-packages that uses the API exposed by the mentioned modules.
So, as a first step I would like to compile that.
Reading: https://openjdk.org/projects/jigsaw/spec/sotms/#module-artifacts
As far as I understand, the code in normal-java-packages should be part of the classpath, so it will be included in the unnamedmodule, and it will able to access all JRE modules as well as my user-defined modules simple-modules/module1 and simple-modules/module2.
I am trying to run:
javac -p simple-modules -d output -cp normal-java-packages
However, it looks like something is off as I get:
error: no source files
Any suggestions or pointers on what may be the issue here?
EDIT:
Also tried this command as suggested:
javac -p simple-modules -d output classpath_entry/com/myuser/modules/main/MainApp.java
Getting these errors:
classpath_entry/com/myuser/modules/main/MainApp.java:3: error: package com.myuser.hello is not visible
import com.myuser.hello.HelloModules;
^
(package com.myuser.hello is declared in module hellomodule, which is not in the module graph)
classpath_entry/com/myuser/modules/main/MainApp.java:4: error: package com.myuser.greetings is not visible
import com.myuser.greetings.EnglishGreeting;
^
(package com.myuser.greetings is declared in module hellomodule, which is not in the module graph)
classpath_entry/com/myuser/modules/main/MainApp.java:5: error: package com.myuser.other is not visible
import com.myuser.other.Other;
^
(package com.myuser.other is declared in module greetings, which is not in the module graph)
3 errors
So it looks like the compiled exploded modules in simple-modules are not visible, any idea why ?
Ok, so I will answer my own question as I found out what the issue is:
the command to use is:
javac -p simple-modules -d output --add-modules ALL-MODULE-PATH classpath_entry/com/myuser/modules/main/MainApp.java
I still find a bit redundant the --add-modules ALL-MODULE-PATH
The JRE modules are loaded fine without it, I don't see why we need it even when we are already specifying the user defined modules in the module path -p already.

Are there any JDK 11+ system modules which are not root modules?

In JDKs 9 and 10, there used to be a few modules such as java.xml.bind, containing Java EE classes. They were marked as deprecated and to be removed with the advent of JDK 9 and finally removed in 11 (see JEP 320). In a product I am contributing to, there used to be tests for the javac compiler option --add-modules, adding those modules as root modules. Those tests have been deactivated for JDK 11+. Instead of removing them, I would like to reactivate them, if there are any other JDK modules which are also non-root by default. The tests could then just use those modules instead.
I know I can just test --add-modules with my own modules, but then I have to specify them on the module path. The test case that an extra module path is not necessary for JDK modules added via --add-modules is also interesting, if any JDK 11+ modules still exist to be tested against. I am not talking about non-exported packages, but really about non-root JDK modules.
So, according to the information in this answer, I am actually looking for non-java.* modules among the system modules which do not export at least one package without qualification. In that case, those modules should not be root, and they would be eligible for my test case.
Update: What I am looking for is an equivalent for this in JDK 9:
import javax.xml.bind.JAXBContext;
public class UsesJAXB {
JAXBContext context;
}
xx> "C:\Program Files\Java\jdk-9.0.4\bin"\javac UsesJAXB.java
UsesJAXB.java:1: error: package javax.xml.bind is not visible
import javax.xml.bind.JAXBContext;
^
(package javax.xml.bind is declared in module java.xml.bind, which is not in the module graph)
1 error
xx> "C:\Program Files\Java\jdk-9.0.4\bin"\javac --add-modules java.xml.bind UsesJAXB.java
See? With --add-modules it builds, without it does not.
I am looking for modules (if any) in JDK 11-18, which would yield the same result when importing their classes in a simple program, i.e. require them to be explicitly added via --add-modules for compilation (not talking about runtime).
You can list all modules of the jdk with:
java --list-modules
Then you can print the module descriptors with:
java --describe-module a.module.name
After filterting these outputs in a little script, here are the modules of my JDK 17 than could qualify:
jdk.charsets
jdk.crypto.cryptoki
jdk.crypto.ec
jdk.editpad
jdk.internal.vm.compiler
jdk.internal.vm.compiler.management
jdk.jcmd
jdk.jdwp.agent
jdk.jlink
jdk.jpackage
jdk.localedata
jdk.zipfs
jdk.charsets, for instance, is a module providing a service.
Update after question update
So you are looking for a module that exports a package but is not in the default module graph when compiling a class in the unnamed module. According to the JEP, only java.* modules can qualify.
When I'm looking for modules required, either directly or indirectly, by java.se (which is a root module when it exists), I see all java.* modules of the JDK, but java.smartcardio. And due to some unknown magic, java.smartcardio is also in the default graph : I tried to compile a class importing one of its class and it works without --add-modules.
So I think you are down to using a non-java module exporting no package (like jdk.charsets), importing one of its class (like sun.nio.cs.ext.ExtendedCharsets) and either:
add --add-exports in addition to --add-modules when compiling your test class so that javac succeeds.
or parse the error message of javac and distinguish between "not in the module graph" and "not exported".

Using JUnit 5 with Java 9 without Maven or Gradle

The Description:
I would like to create a JUnit test using JUnit 5 in Eclipse (Oxygen 4.7.1a).
This JUnit test should be inside a seperate src folder called Test. However, I ran into the following problems as I'm new to JUnit and Java 9.
I do not want to use build tools like Gradle or Maven for this.
The Problem:
As I've got two different src folders, one for the projects src and one for the test cases:
Do I need two module-info.java files? (one for each src folder)
Which modules are required in my module-info.java file for JUnit 5 to work?
In general there is no need to modularize your test code (at least I can't think of a valid reason, maybe someone could give a satisfactory counter-example). Only one module-info.java file can (after all, it's not even required to modularize your main code) be present in your main code under src.
Since the module-info.java file will be only in your main source directory and not in the test source directory, then logically it should not depend on the JUnit module. So the questions now become how to compile and run the JUnit test classes by relying on the module (that represents the system under test) and the JUnit module.
To do that, you'll need to use the new options provided by the javac and java:
So assuming you have the following tree:
src
module-info.java (declares a module called "my.module")
mypackage
MyClass.java
test_src
mypackage
MyClassTest.java
lib/junit-platform-console-standalone.jar
(note: specifically for JUnit 5, you can use the junit-platform-console-standalone artifact that contains the core JUnit engine and allows running tests in the console; see the user guide)
then you can compile the code as follows:
cd root_dir
javac -d mods/my.module src/module-info.java src/mypackage/MyClass.java
cd test_src
javac -d test_out --module-path ../mods;../lib/junit-platform-console-standalone.jar \
--add-modules org.junit.platform.console.standalone,my.module --patch-module my.module=. \
--add-reads my.module=org.junit.platform.console.standalone mypackage/MyClass.java
and you can run the compiled test class:
cd test_src/test_out
java --module-path=../../mods;../../lib/junit-platform-console-standalone.jar \
--add-modules my.module,org.junit.platform.console.standalone \
--add-reads my.module=org.junit.platform.console.standalone \
--patch-module my.module=. \
--add-opens my.module/test=org.junit.platform.console.standalone \
org.junit.platform.console.ConsoleLauncher test.MyClassTest
Awkward commands but that's the cost of not using Maven. I advise you to read about these options in the command documentation after understanding the concept of a module path. An important thing to note here are a couple of options:
--patch-module my.module=.
This is needed because the example test code has the same package (mypackage) as the module my.module. Without it, the module system will complain.
--add-reads my.module=org.junit.platform.console.standalone
This makes junit required by my.module even though it was not declared in module-info.java.
org.junit.platform.console.standalone is the name of the automatic module and is derived from the Jar manifest (as is the case with JUnit 5), otherwise from the name of the Jar file (e.g. in the case of JUnit 4).
Also note that this is what Maven probably does behind the scenes when compiling and running unit tests (see this issue for an equivalent plugin configuration that manually does the above).
What if for some reason, you also want to modularize your unit tests?
In this case, since in the example above the unit tests share the same package, you can include them in my.module and add a requirement to JUnit:
module my.module {
exports mypackage;
requires org.junit.platform.console.standalone;
}
If the unit tests were in a different package, you can also split them into two modules (two module-info.java), a my.module and a my.test.module where only the latter requires JUnit.
If you do include test classes in a module, then in the above commands, you don't need --add-reads and --patch-module.
A reason for the tests being in a module on their own is hexagonal architecture. The test module is a driver adapter swappable with other adapters that run the business logic too.
In my case I'm gonna do it using jaba 9 without maven too.
Here's my article about hexagonal architecture:
https://softwarecampament.wordpress.com/portsadapters/

AspectJ 'thisJoinPoint' throws exception

I'm using AspectJ and the 'ajc' command line compiler. I specify aspectjrt.jar, aspectjtools.jar, and aspectjweaver.jar on the classpath ('-cp') during compilation, yet when I call the standard 'thisJoinPoint', an exception is thrown:
Compilation:
ajc -cp lib/aspectjrt.jar:lib/aspectjtools.jar:lib/aspectjweaver.jar -inpath work/src/ -outjar ./mynewjar.jar #work/source.lst
Code which causes exception:
before() : onCreateCall() {
System.out.println("[-] PC Info: " + thisJoinPoint.getSignature());
}
And the exception itself:
Could not find class 'org.aspectj.runtime.reflect.Factory', referenced from method com.test.WooAspects.ajc$preClinit
Of course, I've tried specifying the import with the following, but no luck:
import org.aspectj.runtime.reflect.Factory;
import org.aspectj.runtime.reflect.*;
Any ideas?
When compiling your code, if it references types in a separate library, that library (possibly packaged as a .jar) needs to be available on the compilation classpath (javac or ajc in this case).
When running your code, if it references types in a separate library, that library needs to be available on the runtime classpath (java or the alternative for aspectj).
Note that an import statement is unrelated to the classpath. All an import statement does is allow you to use a type's or member's short name instead of its fully qualified name.
The following things seem to be a little odd at first glance:
It looks as if you think that -inpath work/src actually is meant to include source files, but the inpath is actually meant to include class files. What you probably want is -sourceroots work/src.
Then you seem to use an argument file named work/source.lst which you have not shown us, so we do not know what is in there - maybe more command line switches, maybe more source files. I have no idea.
On your ajc classpath there are all three AspectJ libraries, but usually you only need aspetcjrt.jar. The other two are only needed for load-time weaving [LTW] (aspectjtools.jar) or if you want to use the AspectJ compiler and a few other tools during runtime (aspectjweaver.jar).
For a simple project in which Java and AspectJ code are in the same source directory, the following works for me (inserting line breaks for better readability, but it is all one line on the console):
ajc
-1.7
-cp lib/aspectjrt.jar
-sourceroots src
-outjar my.jar
Then you run the aspect-enhanced JAR like this (again one line on the console):
java
-cp lib/aspectjrt.jar;my.jar
de.scrum_master.app.Application
I.e. during runtime you also just need the runtime JAR on your classpath.
Maybe you want to use a build tool like Maven managing your dependencies and the build process. You can also use plugins like Maven Shade or One-JAR in order to produce a single über-JAR containing both the compiled Java + AspectJ code and the AspectJ runtime. Then you do not have any problems with classpaths during runtime, you just call
java -jar my_uber.jar
Update: You may want to read the ajc documentation for more info.

Classpath compiles correctly, but will not run. What am I missing

Greetings,
I'm playing around with mahout, I've written a basic java class which imports some of the libraries. It seems my classpath is correct when compiling, I get no errors or complaints at all.
However when I run the compiled class I get an exception saying...
Exception in thread "main" java.lang.NoClassDefFoundError: Test
Caused by: java.lang.ClassNotFoundException: Test
My guess is that . is not on your classpath. For example, you might be compiling with:
javac -cp foo.jar:bar.jar Test.java
but then to run the code you'd need
java -cp foo.jar:bar.jar:. Test
The code that you're compiling doesn't need to be on the classpath as you're providing the code (so there's nothing to find) - that's why it manages to compile but not run.
That's only a guess, of course - if you could post the commands you're using to compile and run the code, that would help.
I'm now getting an error saying java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
You're missing slf4j-api.jar on your class path. With SLF4J, you always need slf4j-api.jar and another jar to bind a logging framework. And actually, if you don't care about logging, use slf4j-nop.jar instead of slf4j-log12.jar.
Update: Mahout seems to be available in Maven central repository so using Maven could ease the class path setup process. And if you're not into learning Maven, consider using MOP which is a command line launcher to run Java stuff that can transparently download Maven artifacts and their dependencies and setup your classpath.
Compile time classpath sounds right; runtime classpath is wrong.
From the javadocs for that class:
Thrown if the Java Virtual Machine or
a ClassLoader instance tries to load
in the definition of a class (as part
of a normal method call or as part of
creating a new instance using the new
expression) and no definition of the
class could be found.
The searched-for class definition
existed when the currently executing
class was compiled, but the definition
can no longer be found.
Do you see a Test.class file in the current directory? Maybe you compiled it to another path by mistake.
If you are using Mahout, be aware that after you build it with Maven, it will generate "*.job" files in the target/ directory, which contain all dependencies packaged together. It is just a .jar file.

Categories

Resources