Packages in JVM languages - java

In Java, classes are placed in a package with a declaration like package com.acme.foo and by putting your source files in a subdirectory like com/acme/foo.
I'm working on a JVM language intended to be more don't repeat yourself in style than Java, so I'm going to use one or other of these mechanisms but not both, and I'm wondering which to use.
How do other JVM languages like Scala and Clojure handle it? Do they require both mechanisms or just one, and if so which one?

As mentioned in the comments to the question Travis Brown is correct, scalac does not place any such constraints or restrictions on the paths or names of files of the source and you can even specify multiple packages in one file if you wish.
However, the bytecode generated by scalac puts the bytecode classes files in the necessary directory structure required by the appropriate classloader.
Here are some examples showing the flexibility (I do not necessarily advocate these styles, just showing the flexibility).
// in packages1.scala file in local directory
package my {
package states {
sealed trait State
case class CheckingOut(shoppingCartId: Long) extends State
case class ConfirmedOrder(orderId: Long) extends State
case class ItemShipped(orderId: Long, itemId: Long, quantity: Int) extends State
}
}
And this...
// in packages2.scala file
package com.foo.bar
sealed trait Scale
case object WebScale extends Scale
case object LolScale extends Scale
case object RoflScale extends Scale
case object WatScale extends Scale
And this style is possible too:
// in packages3.scala file
package foo {
package awesomeness {
class Main extends App {
println("Bananas are awesome")
}
}
}
package foo {
package lameness {
class Main extends App {
println("Congress is pretty lame, honestly")
}
}
}
As well as this...
package foo
package bar
// now we are in package foo.bar for remainder of file unless another package statement is made
Here is the resulting source and compiled bytecode tree:
$ tree
.
├── com
│   └── foo
│   └── bar
│   ├── LolScale$.class
│   ├── LolScale.class
│   ├── RoflScale$.class
│   ├── RoflScale.class
│   ├── Scale.class
│   ├── WatScale$.class
│   ├── WatScale.class
│   ├── WebScale$.class
│   └── WebScale.class
├── foo
│   ├── awesomeness
│   │   ├── Main$delayedInit$body.class
│   │   └── Main.class
│   └── lameness
│   ├── Main$delayedInit$body.class
│   └── Main.class
├── my
│   └── states
│   ├── CheckingOut$.class
│   ├── CheckingOut.class
│   └── State.class
├── packages1.scala
├── packages2.scala
└── packages3.scala
8 directories, 19 files
I am not sure if Clojure supports such flexibility but Clojure convention is to use the Java convention of structuring source code with it's commonly used build tool lein (see leiningen tutorial here).
The one thing to note, however, is that in both Scala and Clojure there appears to be a departure from the $DOMAIN.$APPLICATION format that is often used in the Java world (e.g. com.oracle.jdbc..., org.hibernate.session..., etc). In Scala you will see the $DOMAIN part of the package name being taken out completely (e.g. scalaz..., akka.{actor,io, ...}, etc.).
Also of note is the way you can import from packages in Scala:
Import all public "things" in foo.bar package: import foo.bar._
Import just one class/trait/etc (just like Java): import foo.bar.Baz
Import one class/trait/etc from package and rename it in current scope: import foo.bar.{Baz => FooBarBaz}
Import a subset of things from package: import foo.bar.{Baz, Boo, Bla}
Also of note is package private scoping in Scala:
package foo.bar
class Baz{
private[bar] def boo[A](a: A)
private[foo] def bla[A](a: A)
}
Above boo is private to the foo.bar package (and subpackages) and bla is private to foo and all it's subpackages.
For further details read the Scala language specification and related links:
Section 9.2 "Packagings", 9.3 "Package Objects", 9.4 "Package References"
A Tour of Scala: Packages

Clojure is sort of like Java, but there aren't really classes so it's a bit different.
If there's a namespace project.foo, then the Clojure compiler will expect to find it in the project directory under a source root in a file called foo.clj.
e.g. Assume src is the directory where source files are kept. Then the project.foo namespace would be in src/project/foo.clj with the a declaration like so (ns project.foo) at the top.
I think it's difficult to get away from this really, because at runtime, when Clojure goes to load the file (most Clojure is bundled as source), the Clojure runtime will load the file as a resource, which requires it to be in the right place in the directory hierarchy (whether jar or file based).
Personally, I don't really mind the package name == directory location convention either. It makes it easy for tools to find files either at compile or runtime, and if I'm just using emacs, makes it easy for me to find them as well. It also feels a bit more organized then just a bunch of files in a directory, although that's probably more a matter of what I'm used to after working with it all this time.

I dont have any but a small experience with Clojure but I do have some experience with Scala, and scala uses subdirectories as their packages as well but a nifty feature in it are the ability rename the class similar to the C++ typedef keyword functionality. An interesting idea would be if you created a linker similar to C++'s and allowed a typedef functionality but still keeping it in the JVM.

Related

"ClassNotFoundException" while trying to run .jar file

I have a .jar that I built following the Oracle docs, using jar cfm hangman.jar Manifest.txt src/classes/app/Main.class. The manifest.txt file contains Main-Class as classes.app.Main, telling where my Main class is. When executed, ClassNotFoundException is thrown, saying it couldn't find classes.app.Main. I need help trying to understand what's wrong here. Is it the main class or maybe a missing classpath?
Here's the project tree:
.
├── hangman.jar
├── Manifest.txt
├── README.md
└── src
├── app
│   ├── Main.java
│   ├── Player.java
│   ├── Players.java
│   ├── Play.java
│   ├── Themes.java
│   ├── Word.java
│   └── Words.java
└── classes
└── app
├── Main.class
├── Play.class
├── Player.class
├── Players.class
├── Themes.class
├── Word.class
└── Words.class
You don't show the code, but it is extremely likely that the package for your class is just app not classes.app, and classes is only a directory name to contain the class files, not actually part of the package hierarchy. The name of a class file entry in a jar, OR the name of a class file relative to a classpath directory, must be exactly a directory path equal to the package hierarchy (if any) plus the class name and the suffix .class, with nothing added or removed. This means your jar should be created by going to the classes directory and then adding the file(s) relative to that directory:
jar cfm hangman.jar Manifest.txt -C classes app/Main.class
and the Main-class entry in the manifest should be app.Main. If you only need main-class in the manifest and nothing else (except version, IIRC), you can have jar create it for you:
jar cfe hangman.jar app.Main -C classes app/Main.class
Also I note that there are other classes in your source tree. If these classes are called or referenced from the Main class, directly or indirectly (i.e. nested), they must also be in the jar. You probably want to use app/* instead, although it is possible you want or even need to be more selective.
Meta: I thought this was covered in the standard tutorial, but although most of the pieces are there they aren't really pulled together anyplace I could find and refer to.

Automatically creating tests in intellij idea with multiple test directories

in my project I decided to split unit and integration tests into separate packages and my project structure basically looks like this:
├── main
│   ├── java
│   │   └── ...
│   └── resources
│   └── ...
├── test
│   └── java
│   └── ...
└── test-integration
   ├── java
   │   └── ...
   └── resources
   └── ...
It is a good choice to easily separate two kinds of tests, and intellij refactoring works fine with this solution. Everything is set up correctly, both test directories are marked as test sources roots using gradle's idea plugin, gradle recognizes both source sets, I have two separate tasks created to run each test suite etc. The only problem I have is that intellij automatic create test wizard doesn't differentiate between these two directories, and the only thing I can modify is the Destination package classpath, which obviously is identical for both test directories, and there is no option to choose which one I'd like to use.
Is there any way to configure IDEA to give me an option to choose between directories the test will be generated in? Or maybe some kind of gradle plugin that will move test files based on their suffix (e.g. *Test classes go to test directory, and *IT classes go to test-integration directory)? Or maybe there simply is no such option :D
There is no option to do that. Here's the source for that dialog. Ultimately what determines the directory is this method:
protected static List<VirtualFile> computeTestRoots(#NotNull Module mainModule) {
if (!computeSuitableTestRootUrls(mainModule).isEmpty()) {
//create test in the same module, if the test source folder doesn't exist yet it will be created
return suitableTestSourceFolders(mainModule)
.map(SourceFolder::getFile)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
IntelliJ is very extensible. You could write your own plugin relatively easily which changes this behaviour, by replacing the dialog associated with the action with your own dialog. Your own dialog could mostly be a copy-paste of what's already there, but with a different implementation of selectTargetDirectory.
The next step after "Create Test" dialog should be "Choose destination directory":
The behaviour in v2020.1.1 (https://www.jetbrains.com/idea/download).

How does JPMS knows which packages belong to which modules?

I am trying to get a better understanding of how the new java module system works, especially when it comes to bundling together java packages to their respective modules.
I could not find a way of asking this question in an easy to understand way without tying it to a specific example.
If I have the following project structure:
.
└── src
└── main
└── java
├── module-info.java
└── org
└── lb
└── app
└── App.java
where module-info.java is:
module lb.module {}
and App.java is:
package org.lb.app;
public class App {
public static void main (String[] args) {
System.out.println("Hello, world!");
}
}
and then I run the command from the . directory:
javac -d build/classes $(find src/main/ -name "*.java")
which gives me the following result:
.
├── build
│   └── classes
│   ├── module-info.class
│   └── org
│   └── lb
│   └── app
│   └── App.class
└── src
└── ...
and finally I run the command:
java --module-path build/classes/ --module lb.module/org.lb.app.App
My questions are:
How does the javac tool knows that module-info.class should be in the root directory folder build/classes if there is no "package information" (e.g. like there are in normal java files), and not, for instance in a folder that mimics the original location (e.g. build/classes/src/main/java/module-info.class
I did not expect it to work, but it does. How does the JVM knows that the org.lb.app package belongs to the lb.module module? I purposefully "misnamed" the module, making it not have same name as the directory that contains it, and also did not export the package. It seems obvious for a human reader, but I want to understand what the java program assumes in order to bundle these two together.
Thank you.

NiFi-1.0.0 - load lua script

I have a NiFi processor, that uses the redislabs/luascript lib in order to load a lua script and execute it on a redis instance.
The thing is that I don't know where exactly to put the lua script in order to load it using the luascript lib. I've put it into the nifi_proc/src/main/resources/lua/name.lua, but I get an IOException.
I have a nifi controller service for connecting to redis and a processor that uses that service.
My project structure:
.
├── nifi-bundle-nar
│   └── target
├── nifi-redis_cservice
│   ├── src
│   └── target
├── nifi-redis_cservice-api
│   ├── src
│   └── target
├── nifi-redis_cservice-api-nar
│   └── target
├── nifi-redis_cservice-nar
│   └── target
├── redis-processors
│   ├── src
│   └── target
└── target
└── maven-shared-archive-resources
Any ideas?
Can you share more information about how the processor is interacting with the library? Are you passing in an InputStream, calling out to a executable, etc.?
Ensure your resource is in the JAR module of your processor's project, not the processor's NAR module or the parent (that includes both). You should be able to use getResourceAsStream("lua/name.lua") from a Class object that is in the processor's JAR file (such as the processor class itself). I'm not sure what you'd need to do with it after that, is it possible to share the source code or more details around it?
EDIT (reply to comments below): fromResource() uses LuaScript's classloader to get the resource, I wonder if it doesn't have access to the nifi-proc or controller service resources. It seems like, unless the user needs to specify the location of the script, that the controller service should be loading in the Lua script. So an alternative could be to use the controller service class to getResourceAsStream, read the whole thing into a String, and use fromSource instead of fromResource.

Working with ceylon import-jar

How can I import a library from maven central into a project with the ceylon import-jar command?
Please show the full command.
E.g. for "joda-time-2.9.4.jar" from "http://repo.maven.apache.org/maven2/" into a local directory.
I guess it must be:
./ceylon-1.2.3/bin/ceylon import-jar --rep "http://repo.maven.apache.org/maven2/" --verbose --out localdir "joda-time:joda-time/2.9.4" "joda-time-2.9.4.jar"
But as far as I can see the tool is not working (ceylon versions 1.2.2 and 1.2.3).
Working with maven central is essential.
This question is linked with The ceylon copy tool because both tools present me with a riddle.
I understand you are asking about the ceylon import-jar tool specifically, but would like to offer a different solution that is easier if your goal is to import a jar from a remote repository.
I would suggest you use the Ceylon Gradle Plugin, which I wrote.
It knows how to grab dependencies from repositories (including JCenter and Maven Central, but many others), and it will run the ceylon -import-jar tool for you automatically.
Full Example:
Run the following command to create a new test project (enter simple for the folder name):
ceylon new simple --module-name=com.athaydes.test --module-version=1.0
Enter the new project name and have a look at what's in it (minimum Ceylon project):
cd simple
tree # or use Finder, Window Explorer or whatever
You'll see this:
└── source
└── com
└── athaydes
└── test
├── module.ceylon
├── package.ceylon
└── run.ceylon
Edit module.ceylon so it has the following contents (add whatever dependencies you want):
module com.athaydes.test "1.0" {
native("jvm")
import joda_time.joda_time "2.9.4";
}
Notice the name of the module must be a valid Ceylon identifier! So, the Gradle plugin replaces invalid characters with _, generating a valid Ceylon identifier from the Maven artifact name.
Create a build.gradle file at the root of the project so the Gradle plugin can work, with the following contents:
plugins {
id "com.athaydes.ceylon" version "1.2.0"
}
repositories {
jcenter()
}
ceylon {
module = "com.athaydes.test"
flatClasspath = false
importJars = true
forceImports = true // necessary to bypass optional dependencies issues in Maven poms
}
dependencies {
ceylonCompile "joda-time:joda-time:2.9.4"
}
We must declare this dependency here as a normal Maven dependency so Gradle knows where to get the Jars from.
Done... now just run importJars:
gradle importJars
Or, to just see the actual command generated (will not actually run it):
gradle -P get-ceylon-command importJars
Here's the generated command:
ceylon import-jar
--force
--descriptor=/Users/renato/programming/experiments/ceylon-gradle/simple/build/module-descriptors/joda_time_2.9.4.properties
--out=/Users/renato/programming/experiments/ceylon-gradle/simple/modules
--rep=aether:/Users/renato/programming/experiments/ceylon-gradle/simple/build/maven-settings.xml
--rep=/Users/renato/programming/experiments/ceylon-gradle/simple/modules
joda_time.joda_time/2.9.4
/Users/renato/.gradle/caches/modules-2/files-2.1/joda-time/joda-time/2.9.4/1c295b462f16702ebe720bbb08f62e1ba80da41b/joda-time-2.9.4.jar
The jars will be imported to the default location, modules (but you can configure that):
── build
│   ├── dependency-poms
│   │   └── joda-time-2.9.4.pom
│   ├── maven-repository
│   │   └── joda-time
│   │   └── joda-time
│   │   └── 2.9.4
│   │   ├── joda-time-2.9.4.jar
│   │   └── joda-time-2.9.4.pom
│   ├── maven-settings.xml
│   └── module-descriptors
│   └── joda_time_2.9.4.properties
├── build.gradle
├── modules
│   └── joda_time
│   └── joda_time
│   └── 2.9.4
│   ├── joda_time.joda_time-2.9.4.jar
│   ├── joda_time.joda_time-2.9.4.jar.sha1
│   └── module.properties
└── source
└── com
└── athaydes
└── test
├── module.ceylon
├── package.ceylon
└── run.ceylon
Now you can run the Ceylon code with the runCeylon task (or just run if there's no other task with this name):
gradle run
NOTE:
Unfortunately, actually importing the specific Jar you chose into the Ceylon repo is impossible with its original name... because in Ceylon, joda-time is an illegal identifier... so you need to change the name of the module when imported by Ceylon. The Gradle plugin does it for you.. but you need to know what the valid identifier will be to be able to write the import statement in the module file (you can just let the plugin run and it will tell you what the name will be).
A much simpler approach
If you want to avoid the complexity of this approach, you can just use the default Gradle plugin approach to NOT import Maven jars into the Ceylon repository and just use the simple Java classpath (which means you relinquish using the Ceylon modules system!).
If you do that, your build.gradle file will look like this:
plugins {
id "com.athaydes.ceylon" version "1.2.0"
}
repositories {
jcenter()
}
ceylon {
module = "com.athaydes.test"
}
And the module.ceylon file:
module com.athaydes.test "1.0" {
native("jvm")
import "joda-time:joda-time" "2.9.4";
}
Notice that we don't need to mess up with the dependency name using this approach. From Ceylon 1.2.3, you should prepend the dependency with the maven: qualifier to avoid warnings.
That simple!
1. As a (partial) answer to my question, this turned out to work:
$ ../bin/ceylon import-jar --rep flat:"../flat/" Jama/1.0.3 ../flat/Jama-1.0.3.jar
I downloaded the jar (in this case Jama-1.0.3.jar) by hand and then I was able to import it.
I had to try a lot to find out where to put the prefix "flat:", i.e. either to put it after "import" in the module descriptor "module.ceylon" or on the command line. The latter turned out to be the right choice.
But still, I haven't been able to find out how to import the jar from maven directly using the import-jar tool.
2. More detailed documentation is needed about managing modules. Specifically, there should be a clarification what the term "legacy repository" means.
Does "legacy" mean "deprecated"?
3. I hope that the following way to import dependencies into a project is not considered as "legacy" or "deprecated":
a) Rename the jar file, so that the name relfects the compressed directory structure within the jar.
b) Put the jar into a directory structure that again reflects the directory structure within the jar.
c) Put all that into the modules directory of the project, merging directories if necessary.
This seems to be the most explicit and reliable way to include dependencies into a project and I hope this way will not be deprecated or considered "legacy" at any time.

Categories