From d5e1c15cd251cec1c3d63f451563e22a96d9b44b Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 20:43:47 -0400 Subject: [PATCH 01/14] Fix minor grammatical errors --- _clojure_jvm/01_jvm.md | 4 ++-- _clojure_jvm/02_basic_java.md | 2 +- _clojure_jvm/03_class_files.md | 4 ++-- _clojure_jvm/04_jar_files.md | 6 +++--- _clojure_jvm/05_dependencies.md | 14 +++++++------- _clojure_jvm/06_common_issues.md | 6 +++--- _clojure_jvm/07_evaluating_clojure.md | 2 +- _clojure_jvm/08_compiling_clojure.md | 4 ++-- _clojure_jvm/09_deps.md | 4 ++-- 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/_clojure_jvm/01_jvm.md b/_clojure_jvm/01_jvm.md index 651f8d3..46b16ef 100644 --- a/_clojure_jvm/01_jvm.md +++ b/_clojure_jvm/01_jvm.md @@ -12,8 +12,8 @@ Each release of the Java platform has a corresponding JDK for development. ## Java Virtual Machine -The JVM is a virtual machine which defines its own instruction set, referred to a Java bytecode. The process of locating, loading, verifying and executing this bytecode is -define by the [JVM specification](https://docs.oracle.com/javase/specs/jvms/se20/html/index.html). After installing a Java distribution, the JVM can be invoked via the `java` +The JVM is a virtual machine which defines its own instruction set, referred to as Java bytecode. The process of locating, loading, verifying and executing this bytecode is +defined by the [JVM specification](https://docs.oracle.com/javase/specs/jvms/se20/html/index.html). After installing a Java distribution, the JVM can be invoked via the `java` command. ## Java Development Kit diff --git a/_clojure_jvm/02_basic_java.md b/_clojure_jvm/02_basic_java.md index bb2ae33..e51ecfe 100644 --- a/_clojure_jvm/02_basic_java.md +++ b/_clojure_jvm/02_basic_java.md @@ -26,7 +26,7 @@ This outputs a `Hello.class` file in the `./test` directory. The `main` method c Java program execution begins from a named class specified to the java runtime. In this previous example this was the class `test.Hello`. The JVM loads this class and executes its `main` method, which must have the signature `public static void main(String[])` i.e. a static -method called `main` which takes a single string array parameter. This array receives all the argument provided to the `java` command which +method called `main` which takes a single string array parameter. This array receives all the arguments provided to the `java` command which do not configure the execution of the JVM itself. ## Loading classes diff --git a/_clojure_jvm/03_class_files.md b/_clojure_jvm/03_class_files.md index eca77c5..265f554 100644 --- a/_clojure_jvm/03_class_files.md +++ b/_clojure_jvm/03_class_files.md @@ -88,7 +88,7 @@ Within class files, descriptors are used to define the types of fields and metho #### Field descriptors JVM types are either one of the primitive types, an object type or an array type. The grammar for -fields descriptors denote one of these three possibilities. +field descriptors denotes one of these three possibilities. The primitive types are `byte`, `char`, `double`, `float`, `int`, `long`, `short` and `boolean` and these are denoted as `B`, `C`, `D`, `F`, `I`, `J`, `S` and `Z` respectively within the field descriptor grammar. For example, a descriptor of `J` indicates a field of the primitive `long` type. @@ -176,7 +176,7 @@ public test.Hello(); line 3: 0 ``` -The constructor declares no format parameters, and returns `void` as shown by the descriptor. It does require a single parameter however - the +The constructor declares no formal parameters, and returns `void` as shown by the descriptor. It does require a single parameter however - the reference to the new object being initialised. This is loaded onto the operand stack with the [aload_0](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.aload_n) instruction. The `java.lang.Object` constructor is then invoked in the same way, passing the reference just loaded as the first argument. The [invokespecial](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.invokespecial) is similar to `invokevirtual` and `invokestatic` but is required to invoke constructors and superclass methods. The operand to the `invokespecial` instruction is an index into the constant pool diff --git a/_clojure_jvm/04_jar_files.md b/_clojure_jvm/04_jar_files.md index ff7fcbd..a4cf42f 100644 --- a/_clojure_jvm/04_jar_files.md +++ b/_clojure_jvm/04_jar_files.md @@ -30,7 +30,7 @@ __src/libhello/MessageSink.java__ {% include code/jar/src/libhello/MessageSink.java %} ``` -In addition, we define a source of messages read from the command-line, and a sink which writes messages a `PrintStream`: +In addition, we define a source of messages read from the command-line, and a sink which writes messages to a `PrintStream`: __src/libhello/CommandLineMessageSource.java__ ```java @@ -103,7 +103,7 @@ which we can then execute by placing both the lib and app JARs on the classpath: ## Manifest files When listing the contents of the JAR file above, we saw a `META-INF/MANIFEST.MF` file included. The `META-INF` directory within a JAR contains -files used to configure aspects of the JVM. The `MANIFEST.MF` file contain various sections of key-value pairs used to describe the contents of the JAR. +files used to configure aspects of the JVM. The `MANIFEST.MF` file contains various sections of key-value pairs used to describe the contents of the JAR. The format of the `META-INF` directory and `MANIFEST.MF` files are described in detail within the [JAR file specification](https://docs.oracle.com/en/java/javase/20/docs/specs/jar/jar.html). The `jar` tool can be used to extract the contents of the default manifest file: @@ -168,7 +168,7 @@ and run it with the `-jar` option as before: ### File collisions When building the uberjar above, we simply overwrote (using the `-o` option for `unzip`) any existing files within `libhello.jar` -when extracting the `echo.jar` file. This file for our simple application since there are no collisions, except for the default manifest +when extracting the `echo.jar` file. This is fine for our simple application since there are no collisions, except for the default manifest files in the two files, which should be the same. However, this simple strategy may not work for more complicated applications. It's possible that different JAR files will contain different files at the same path which need special handling to produce the corresponding file in the output JAR. One example is libraries which configure [ServiceLoader](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) diff --git a/_clojure_jvm/05_dependencies.md b/_clojure_jvm/05_dependencies.md index 2cf7100..0e5c501 100644 --- a/_clojure_jvm/05_dependencies.md +++ b/_clojure_jvm/05_dependencies.md @@ -62,7 +62,7 @@ the `org.json.JSONTokener` class. This is not surprising, since given the `libhe on `json-java.jar` at runtime. JAR files are just an archive of class files and do not define a mechanism for declaring which other JARs define dependency classes. -In addition, the other team have a few other requirements for consuming our library: +In addition, the other team has a few other requirements for consuming our library: * They would prefer not to commit binary files to their source control repository * They would like to be able to identify the library version from the JAR file name @@ -116,12 +116,12 @@ projects can reference dependencies published to this repository without any fur #### Local repositories -During dependency resolution, Maven fetches any required dependencies and copies them to a local repository. By default, this repository is located at `~/.m2/repository` and follows the same layout at described above. +During dependency resolution, Maven fetches any required dependencies and copies them to a local repository. By default, this repository is located at `~/.m2/repository` and follows the same layout as described above. Therefore, version `0.2.6` of the `data.json` JAR would be fetched to `~/.m2/repository/org/clojure/data.json/0.2.6/data.json-0.2.6.jar`. ## Maven library development -Now we understand the basics of Maven we can create a project for our library. First we create a directory for the project and set it up in the +Now that we understand the basics of Maven we can create a project for our library. First we create a directory for the project and set it up in the standard Maven layout. As defined in the [super POM](https://maven.apache.org/ref/3.0.4/maven-model-builder/super-pom.html) Java source files go under `${project.basedir}/src/main/java`. @@ -160,7 +160,7 @@ Both the JAR and POM files are published so the dependency information is still ## Maven application development -With the library published to our local repository, we decide to convert our test application into a Maven project as well. Again we setup the directory +With the library published to our local repository, we decide to convert our test application into a Maven project as well. Again we set up the directory layout and POM files: ``` @@ -179,11 +179,11 @@ json-test/ {% include code/java_dependencies/app/pom.xml %} ``` -The application POM declares a dependency on the version of the library we want to use. As before the JAR can be build with +The application POM declares a dependency on the version of the library we want to use. As before the JAR can be built with mvn package -which creates the JAR in the `json-test-1.0.0.jar`. When we try to run the application with +which creates the JAR `json-test-1.0.0.jar`. When we try to run the application with java -cp target/json-test-1.0.0.jar test.EchoJSON messages.json @@ -215,7 +215,7 @@ Another option is to build uber JARs using the [Maven shade plugin](https://mave Now the library and application are working locally, it's time to share the library with our colleagues. Our chief architect is convinced it constitutes a key competitive advantage for the company and is unwilling to unleash it on an unsuspecting public. She arranges for a private -Maven repository to be setup, and our CI process to publish there instead of the Maven central repository. We advise any teams wishing to use +Maven repository to be set up, and our CI process to publish there instead of the Maven central repository. We advise any teams wishing to use it to configure the private repository in their application POM files with the following fragment: ```xml diff --git a/_clojure_jvm/06_common_issues.md b/_clojure_jvm/06_common_issues.md index 0f896dd..57e5947 100644 --- a/_clojure_jvm/06_common_issues.md +++ b/_clojure_jvm/06_common_issues.md @@ -23,8 +23,8 @@ can be quite large, and it's not uncommon to come across unknown classes deep in #### Statically Most project management tools such as Maven, Leiningen or tools.deps have tasks to output the current classpath. There are usually -multiple classpaths within a project depending on the context. A common example is adding extra dependencies are resource directories -when running tests. As we saw previously, the classpath for a Maven project can be retreived with +multiple classpaths within a project depending on the context. A common example is adding extra dependencies or resource directories +when running tests. As we saw previously, the classpath for a Maven project can be retrieved with mvn dependency:build-classpath -Dmdep.outputFile=classpath.txt @@ -115,7 +115,7 @@ somewhere in your dependency list. ## ServiceLoader errors -Java [ServiceLoaders](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/ServiceLoader.html) an extensible mechanism for +Java [ServiceLoaders](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/ServiceLoader.html) are an extensible mechanism for locating and loading service classes at runtime. They work by first defining an interface or class e.g. ```java diff --git a/_clojure_jvm/07_evaluating_clojure.md b/_clojure_jvm/07_evaluating_clojure.md index 93e8d01..7ba851b 100644 --- a/_clojure_jvm/07_evaluating_clojure.md +++ b/_clojure_jvm/07_evaluating_clojure.md @@ -31,7 +31,7 @@ Hello world! ### Locating Clojure files -Most Clojure code is written to file and located and evaluated by the Clojure runtime during program execution. Similar to Java class files, +Most Clojure code is written to files and located and evaluated by the Clojure runtime during program execution. Similar to Java class files, Clojure source files are located on the classpath. When a namespace is loaded, the namespace name is converted to a set of relative locations on the classpath the namespace could be defined in. The conversion process is as follows: diff --git a/_clojure_jvm/08_compiling_clojure.md b/_clojure_jvm/08_compiling_clojure.md index 520254c..d45a5e0 100644 --- a/_clojure_jvm/08_compiling_clojure.md +++ b/_clojure_jvm/08_compiling_clojure.md @@ -38,7 +38,7 @@ The generated class methods simply delegate to Clojure functions with the approp of `greeter-`, so its `greet` method delegates to the function `greeter-greet` within the implementation namespace (by default the current namespace). Similarly, the `main` method of the `greet.main` class should be defined by a `-main` function. -The `main` class can now be invoked as with another other main Java class: +The `main` class can now be invoked as with any other main Java class: ``` > java -cp classes:clojure-1.11.1.jar:core.specs.alpha-0.2.62.jar:spec.alpha-0.3.218.jar greet.main everyone @@ -76,7 +76,7 @@ Hello everyone! ## Clojure uberjars -Now we can create Java entrypoints for our Clojure applications, we can create 'fat' JARs by including the Clojure JARs along with +Now that we can create Java entrypoints for our Clojure applications, we can create 'fat' JARs by including the Clojure JARs along with the application source files and any compiled classes. As before, we need to define a manifest file which specifies the main class **manifest.mf** diff --git a/_clojure_jvm/09_deps.md b/_clojure_jvm/09_deps.md index ffe7df7..93d1ef4 100644 --- a/_clojure_jvm/09_deps.md +++ b/_clojure_jvm/09_deps.md @@ -93,7 +93,7 @@ This means we could use these defaults in our project and our project `deps.edn` ## Aliases At different points of the development process, you might want to make additional dependencies available or add more directories to the classpath. -This can be done by specifying _aliases_ with in the project `deps.edn` and supplying them to the `clojure` command where required. For example, during +This can be done by specifying _aliases_ within the project `deps.edn` and supplying them to the `clojure` command where required. For example, during development, you might want to use [scope capture](https://github.com/vvvvalvalval/scope-capture) to help with debugging at the REPL. This isn't a dependency of the main application, so shouldn't be distributed with it. @@ -118,7 +118,7 @@ user=> The Clojure JARs are published to Maven central so are declared as Maven dependencies in the previous `deps.edn`. Deps supports other dependency types such as Git repositories and local directories. -We've decided to improve our greeting application by adding an optional command-line flag to show enthusiasm (or not). We also decide to split the +We've decided to improve our greeting application by adding an optional command-line flag to show enthusiasm (or not). We also decided to split the core greeting functionality into its own library. Since it's under active development, we just want to refer to it locally for now before we're ready to publish the first version. From c325948251fcff5391b13c8cd4a014916f9382d1 Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 20:49:50 -0400 Subject: [PATCH 02/14] Minor rewording for clarity --- _clojure_jvm/02_basic_java.md | 2 +- _clojure_jvm/03_class_files.md | 10 +++++----- _clojure_jvm/05_dependencies.md | 6 ++---- _clojure_jvm/06_common_issues.md | 6 +++--- _clojure_jvm/07_evaluating_clojure.md | 2 +- _clojure_jvm/08_compiling_clojure.md | 4 ++-- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/_clojure_jvm/02_basic_java.md b/_clojure_jvm/02_basic_java.md index e51ecfe..b46ab77 100644 --- a/_clojure_jvm/02_basic_java.md +++ b/_clojure_jvm/02_basic_java.md @@ -24,7 +24,7 @@ This outputs a `Hello.class` file in the `./test` directory. The `main` method c ## Entry point -Java program execution begins from a named class specified to the java runtime. In this previous example this was the class `test.Hello`. +Java program execution begins from a named class specified to the java runtime. In the previous example this was the class `test.Hello`. The JVM loads this class and executes its `main` method, which must have the signature `public static void main(String[])` i.e. a static method called `main` which takes a single string array parameter. This array receives all the arguments provided to the `java` command which do not configure the execution of the JVM itself. diff --git a/_clojure_jvm/03_class_files.md b/_clojure_jvm/03_class_files.md index 265f554..9057598 100644 --- a/_clojure_jvm/03_class_files.md +++ b/_clojure_jvm/03_class_files.md @@ -68,8 +68,8 @@ which means it is only supported by versions 14 or higher of the JVM. Attempting ## Class properties -The `flags`, `this_class` and `super_class` entries define the access properties and names of the defined class and its direct superclass. The `Hello` does not explicitly -declare a superclass, so it is implicitly `java.lang.Object`. +The `flags`, `this_class` and `super_class` entries define the access properties and names of the defined class and its direct superclass. The `Hello` class does not explicitly +declare a superclass, so it is implicitly a subclass of `java.lang.Object`. ## Constant pool @@ -90,7 +90,7 @@ Within class files, descriptors are used to define the types of fields and metho JVM types are either one of the primitive types, an object type or an array type. The grammar for field descriptors denotes one of these three possibilities. -The primitive types are `byte`, `char`, `double`, `float`, `int`, `long`, `short` and `boolean` and these are denoted as `B`, `C`, `D`, `F`, `I`, `J`, `S` and `Z` respectively within +The primitive types are `byte`, `char`, `double`, `float`, `int`, `long`, `short` and `boolean`, denoted as `B`, `C`, `D`, `F`, `I`, `J`, `S` and `Z` respectively within the field descriptor grammar. For example, a descriptor of `J` indicates a field of the primitive `long` type. Object types are defined with the literal `L` followed by the binary name of the class type followed by the literal ';'. For example a field of type `java.lang.Thead` has a descriptor of `Ljava/lang/Thread;`. @@ -178,7 +178,7 @@ public test.Hello(); The constructor declares no formal parameters, and returns `void` as shown by the descriptor. It does require a single parameter however - the reference to the new object being initialised. This is loaded onto the operand stack with the [aload_0](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.aload_n) -instruction. The `java.lang.Object` constructor is then invoked in the same way, passing the reference just loaded as the first argument. The [invokespecial](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.invokespecial) +instruction. The `java.lang.Object` constructor is then invoked in the same way, passing the reference just loaded as the first argument. The [invokespecial](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.invokespecial) instruction is similar to `invokevirtual` and `invokestatic` but is required to invoke constructors and superclass methods. The operand to the `invokespecial` instruction is an index into the constant pool of the `test.Hello` class. The element at index `#1` is a method reference to the `java/lang/Object.` method as indicated by the comment. @@ -205,7 +205,7 @@ The flags also indicate the method is `static` and `public`. The [getstatic](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.getstatic) instruction, loads a field reference onto the operand stack. The operand of `#7` is the index of the reference to the `System.out` field within the constant pool of the `test.Hello` class. -The literal string "Hello world!" is pushed onto the operand stack with the [ldc](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.ldc) instruction. The operand of +The string "Hello world!" is pushed onto the operand stack with the [ldc](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.ldc) instruction. The operand of `#13` is the index of the string within the class constant pool. At this point the operand stack contains the arguments to the `java.io.PrintStream.` method - the receiver (the contents of the `System.out` field), and the string to write. The diff --git a/_clojure_jvm/05_dependencies.md b/_clojure_jvm/05_dependencies.md index 0e5c501..443676d 100644 --- a/_clojure_jvm/05_dependencies.md +++ b/_clojure_jvm/05_dependencies.md @@ -103,12 +103,10 @@ which can be used to locate an artifact based on its co-ordinates. For most arti /$groupId[0]/../${groupId[n]/$artifactId/$version/$artifactId-$version.$extension where `$groupId[0]..groupId[n]` are the dot-separated components of the publisher's groupId. For example artifacts for the `org.clojure` group will be published under `org/clojure`. The `$extension` component is -determined by the artifact packaging type - this is usually `jar` for Java artifacts. For example, the `data.json` project is published by the `org.clojure` group, so the JAR for version `0.2.6` would be located at: +determined by the artifact packaging type - this is usually `jar` for Java artifacts. For example, the `data.json` project is published by the `org.clojure` group, so the JAR for version `0.2.6` would be located within a repository at: org/clojure/data.json/0.2.6/data.json-0.2.6.jar -within a repository. - #### Maven central Maven defines a single repository `central` in the [super POM](https://maven.apache.org/ref/3.0.4/maven-model-builder/super-pom.html). This is located at http://repo.maven.apache.org/maven2 and it means that Maven @@ -224,7 +222,7 @@ it to configure the private repository in their application POM files with the f ### Repository settings -Public repositories can be accessed anonymously, but our private repository requires authentication to access. Since authentication is connected +Public repositories can be accessed anonymously, but our private repository requires authentication. Since authentication is connected to a user instead of a project, Maven allows user settings to be configured outside of a project. By default these settings are defined within a `~/.m2/settings.xml` file. The full format of the file is specified in the [documentation](https://maven.apache.org/settings.html), but for our private repository, we just need to configure the credentials to use: diff --git a/_clojure_jvm/06_common_issues.md b/_clojure_jvm/06_common_issues.md index 57e5947..640d172 100644 --- a/_clojure_jvm/06_common_issues.md +++ b/_clojure_jvm/06_common_issues.md @@ -105,7 +105,7 @@ the `-verbose:class` option to the JVM This outputs the location each class is loaded from. Writing this to file or filtering with `grep` should allow you to find where the class is located on the classpath. This will usually be a JAR file, and if fetched from a Maven repository -will contain the artifact name and version number in the name. +will contain the artifact name and version number in the filename. The next step is deciding where you expect the class to be loaded from - this might be a different library entirely, or a different version of the same library. @@ -146,8 +146,8 @@ com.picosoft.messaging.db.MySqlMessageSourceFactory when the implementation JAR is on the classpath these classes can be located and loaded via the `ServiceLoader` interface. Special care must be taken when building an uberjar containing service loader classes. Multiple implementation JARs will define -the same `META-INF/services/{service.class}` file. The default conflict handling behaviour when building uberjars is 'last one wins' for -files at the same path. For service loaders, conflicting files should be concatenated together so all implementation classes are +the same `META-INF/services/{service.class}` file. When building uberjars, the default conflict handling behaviour for +files at the same path is 'last one wins'. For service loaders, conflicting files should be concatenated together so all implementation classes are listed in the final JAR. ## Uberjar path conflicts diff --git a/_clojure_jvm/07_evaluating_clojure.md b/_clojure_jvm/07_evaluating_clojure.md index 7ba851b..d964597 100644 --- a/_clojure_jvm/07_evaluating_clojure.md +++ b/_clojure_jvm/07_evaluating_clojure.md @@ -33,7 +33,7 @@ Hello world! Most Clojure code is written to files and located and evaluated by the Clojure runtime during program execution. Similar to Java class files, Clojure source files are located on the classpath. When a namespace is loaded, the namespace name is converted to a set of relative locations on the -classpath the namespace could be defined in. The conversion process is as follows: +classpath where the namespace could be defined. The conversion process is as follows: 1. Convert any `.` characters in the namespace name to `/`s 2. Convert any `-` characters in the namespace name to `_`s diff --git a/_clojure_jvm/08_compiling_clojure.md b/_clojure_jvm/08_compiling_clojure.md index d45a5e0..e9ca735 100644 --- a/_clojure_jvm/08_compiling_clojure.md +++ b/_clojure_jvm/08_compiling_clojure.md @@ -3,7 +3,7 @@ layout: post title: Compiling Clojure --- -We saw previously how Clojure source code is located on the classpath, read and evaluated. It also supports defining classes which can be compiled +We saw previously how Clojure source code is located on the classpath, read and evaluated. Clojure also supports defining classes which can be compiled into `.class` files with [gen-class](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/gen-class). The following example defines two classes with `gen-class`: @@ -38,7 +38,7 @@ The generated class methods simply delegate to Clojure functions with the approp of `greeter-`, so its `greet` method delegates to the function `greeter-greet` within the implementation namespace (by default the current namespace). Similarly, the `main` method of the `greet.main` class should be defined by a `-main` function. -The `main` class can now be invoked as with any other main Java class: +The `main` class can now be invoked as with any other Java main class: ``` > java -cp classes:clojure-1.11.1.jar:core.specs.alpha-0.2.62.jar:spec.alpha-0.3.218.jar greet.main everyone From 16ec0997d74f1363388a034972faf853adf82442 Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 21:03:59 -0400 Subject: [PATCH 03/14] Adjust list of `.class` file sections to match order of sections in the document (and `javap` output) --- _clojure_jvm/03_class_files.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_clojure_jvm/03_class_files.md b/_clojure_jvm/03_class_files.md index 9057598..5ed17b6 100644 --- a/_clojure_jvm/03_class_files.md +++ b/_clojure_jvm/03_class_files.md @@ -37,8 +37,8 @@ They contain a sequence of sections 1. [Magic number](#magic-number) 2. [Version number](#version-number) -3. [Constant pool](#constant-pool) -4. [Class properties](#class-properties) +3. [Class properties](#class-properties) +4. [Constant pool](#constant-pool) 5. Interfaces 6. Fields 7. [Methods](#methods) From 2d751bcf201b212fd79dd031b5c00da832771f32 Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 21:59:19 -0400 Subject: [PATCH 04/14] Consistently use fully-qualified class name `test.Hello` --- _clojure_jvm/03_class_files.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_clojure_jvm/03_class_files.md b/_clojure_jvm/03_class_files.md index 5ed17b6..f61d535 100644 --- a/_clojure_jvm/03_class_files.md +++ b/_clojure_jvm/03_class_files.md @@ -68,7 +68,7 @@ which means it is only supported by versions 14 or higher of the JVM. Attempting ## Class properties -The `flags`, `this_class` and `super_class` entries define the access properties and names of the defined class and its direct superclass. The `Hello` class does not explicitly +The `flags`, `this_class` and `super_class` entries define the access properties and names of the defined class and its direct superclass. The `test.Hello` class does not explicitly declare a superclass, so it is implicitly a subclass of `java.lang.Object`. ## Constant pool From 8b3bc9fa9e131f142959e387050347d78c4e30bf Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 21:32:49 -0400 Subject: [PATCH 05/14] Minor: `classloader` => `class loader` --- _clojure_jvm/02_basic_java.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_clojure_jvm/02_basic_java.md b/_clojure_jvm/02_basic_java.md index b46ab77..109bd36 100644 --- a/_clojure_jvm/02_basic_java.md +++ b/_clojure_jvm/02_basic_java.md @@ -72,12 +72,12 @@ change back to the parent directory where the `Hello.java` file is defined ### System classes The `test.Hello` class makes reference to another class `java.lang.System` which is defined by the core Java library. Since there's no `java/lang/System.class` file -under the current directory, you might wonder how it is loaded. The answer is that the bootstrap classloader has its own internal mechanism for locating core library classes. -The only requirement for classloaders is that they can resolve and load classes given their full (binary) names. There is no requirement that these exist as `.class` files on -disk. They can be loaded from the network, a database or defined dynamically depending on how the classloader is implemented. The bootstrap classloader knows where to locate +under the current directory, you might wonder how it is loaded. The answer is that the bootstrap class loader has its own internal mechanism for locating core library classes. +The only requirement for class loaders is that they can resolve and load classes given their full (binary) names. There is no requirement that these exist as `.class` files on +disk. They can be loaded from the network, a database or defined dynamically depending on how the class loader is implemented. The bootstrap class loader knows where to locate core Java classes and loads them as required. -Older JVMs (before version 9) did ship core Java classes in a Java archive. This was usually located at `jre/lib/rt.jar` within the JVM distribution. The bootstrap classloader +Older JVMs (before version 9) did ship core Java classes in a Java archive. This was usually located at `jre/lib/rt.jar` within the JVM distribution. The bootstrap class loader was additionally configured with a 'bootstrap classpath' containing this core archive. Since the advent of Java modules in Java 9, core classes are distributed in a more efficient format. From 4aab4c430ef163fc9141e708f4948671dc361bd5 Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 21:21:51 -0400 Subject: [PATCH 06/14] Minor: `ns function` => `ns macro` --- _clojure_jvm/08_compiling_clojure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_clojure_jvm/08_compiling_clojure.md b/_clojure_jvm/08_compiling_clojure.md index e9ca735..e31bb04 100644 --- a/_clojure_jvm/08_compiling_clojure.md +++ b/_clojure_jvm/08_compiling_clojure.md @@ -47,7 +47,7 @@ Hello everyone! ## Namespace compilation -The `ns` function used to define namespaces supports an optional `:gen-class` option to compile a class for the namespace. +The `ns` macro used to define namespaces supports an optional `:gen-class` option to compile a class for the namespace. It supports all the options of [gen-class](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/gen-class) and by default uses the namespace name as the class name, sets `:main` to `true`, and sets the prefix to '-'. This means a `-main` function is expected to exist which corresponds to the `main` method of the generated class. From 89a9026164e95ad952aeb727242da55299ef5b0b Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 21:36:17 -0400 Subject: [PATCH 07/14] Minor: `co-ordinates` => `coordinates` --- _clojure_jvm/05_dependencies.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_clojure_jvm/05_dependencies.md b/_clojure_jvm/05_dependencies.md index 443676d..5c4f267 100644 --- a/_clojure_jvm/05_dependencies.md +++ b/_clojure_jvm/05_dependencies.md @@ -85,7 +85,7 @@ the standard layout of a Maven project. Most projects make use of external libraries, which often in turn declare dependencies of their own. As we've seen, manually managing these dependencies and putting them on the classpath rapidly becomes impractical, even for small projects. Maven allows projects to declare which libraries they depend on, -and manages fetching these locally and constructing the required classpath. Dependencies are identified by a set of _co-ordinates_ which can be used to +and manages fetching these locally and constructing the required classpath. Dependencies are identified by a set of _coordinates_ which can be used to locate them within a repository they're published to. These consist of three main components: * Group - The entity or organisation responsible for maintaining and publishing the artifact. These are usually identified by a reversed domain name controlled by the publisher, such as `org.apache` or `com.google`. @@ -98,7 +98,7 @@ specify other components such as a classifier or different type - see [Maven Coo ### Repositories In order to make artifacts available to others, the responsible organisation publishes them to a _repository_. Maven repositories have a [defined layout](https://cwiki.apache.org/confluence/display/MAVENOLD/Repository+Layout+-+Final) -which can be used to locate an artifact based on its co-ordinates. For most artifacts, the location is given by: +which can be used to locate an artifact based on its coordinates. For most artifacts, the location is given by: /$groupId[0]/../${groupId[n]/$artifactId/$version/$artifactId-$version.$extension @@ -135,7 +135,7 @@ libhello/ ... ``` -Our `pom.xml` file defines the co-ordinates for the library itself along with its dependencies: +Our `pom.xml` file defines the coordinates for the library itself along with its dependencies: **pom.xml** ```xml @@ -153,7 +153,7 @@ This can be installed into our local repository with mvn install -As we would expect from the library co-ordinates and the default local repository location, this is installed into `~/.m2/repository/com/enterprisey/libhello/1.0.0/`. +As we would expect from the library coordinates and the default local repository location, this is installed into `~/.m2/repository/com/enterprisey/libhello/1.0.0/`. Both the JAR and POM files are published so the dependency information is still accessible. ## Maven application development From 41ed188176f64aa6ccc5d595e94a70fe8afbb4f9 Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 20:55:53 -0400 Subject: [PATCH 08/14] Punctuation --- _clojure_jvm/03_class_files.md | 12 ++++++------ _clojure_jvm/04_jar_files.md | 2 +- _clojure_jvm/05_dependencies.md | 10 +++++----- _clojure_jvm/06_common_issues.md | 2 +- _clojure_jvm/09_deps.md | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/_clojure_jvm/03_class_files.md b/_clojure_jvm/03_class_files.md index f61d535..74bd738 100644 --- a/_clojure_jvm/03_class_files.md +++ b/_clojure_jvm/03_class_files.md @@ -103,7 +103,7 @@ has a descriptor of `[I`. Methods take a (possibly empty) collection of arguments and optionally return a value. Methods which do not return a value are given a return type of `void` (denoted by `V`) within method descriptors. Methods which return a value use the grammar for field descriptors to denote the return type. -Method descriptors consist of a literal `(`, the corresponding field descriptor for each parameter type in sequence, a literal `)` followed by the return type descriptor. This can be either `V` for `void` methods, +Method descriptors consist of a literal `(`, the corresponding field descriptor for each parameter type in sequence, a literal `)` followed by the return type descriptor. This can be either `V` for `void` methods or a field descriptor for the return type. For example the method `public String test(int i, boolean b, Object o)` has a method descriptor of `(IZLjava/lang/Object;)Ljava/lang/String;`. @@ -131,7 +131,7 @@ Entries in the method table define the name, attributes (access modifiers etc.) ### Instruction format -JVM instructions are variable-length and consist of an `opcode` which defines the operation, and a (possibly empty) sequence of operands. `javap` displays +JVM instructions are variable-length and consist of an `opcode`, which defines the operation, and a (possibly empty) sequence of operands. `javap` displays each instruction in the following format: [ , ..., ] [ ] @@ -150,9 +150,9 @@ store values from the top of the stack into local variables, and some consume mu #### Arguments -In order to invoke a method, its arguments are pushed in order onto the operand stack, and the method is invoked via a method reference in the class constant pool. +In order to invoke a method, its arguments are pushed in order onto the operand stack and the method is invoked via a method reference in the class constant pool. -Instance methods are invoked via the `invokevirtual` instruction, and have a first argument which is a reference to the object the method is being invoked on (the receiver). +Instance methods are invoked via the `invokevirtual` instruction and have a first argument which is a reference to the object the method is being invoked on (the receiver). Static methods are invoked with `invokestatic` and only the explicit arguments are pushed prior to invocation. Within a method, the arguments are loaded onto the operand stack with a family of opcode instructions (`aload`, `iload` etc.). @@ -176,7 +176,7 @@ public test.Hello(); line 3: 0 ``` -The constructor declares no formal parameters, and returns `void` as shown by the descriptor. It does require a single parameter however - the +The constructor declares no formal parameters and returns `void` as shown by the descriptor. It does require a single parameter however - the reference to the new object being initialised. This is loaded onto the operand stack with the [aload_0](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.aload_n) instruction. The `java.lang.Object` constructor is then invoked in the same way, passing the reference just loaded as the first argument. The [invokespecial](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.invokespecial) instruction is similar to `invokevirtual` and `invokestatic` but is required to invoke constructors and superclass methods. The operand to the `invokespecial` instruction is an index into the constant pool @@ -202,7 +202,7 @@ public static void main(java.lang.String[]); As indicated by the descriptor, The `main` method defines a single `String[]` parameter and has a return type of `void`. The flags also indicate the method is `static` and `public`. -The [getstatic](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.getstatic) instruction, loads a field reference onto the operand stack. The operand of `#7` is the index of the reference to the +The [getstatic](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.getstatic) instruction loads a field reference onto the operand stack. The operand of `#7` is the index of the reference to the `System.out` field within the constant pool of the `test.Hello` class. The string "Hello world!" is pushed onto the operand stack with the [ldc](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.ldc) instruction. The operand of diff --git a/_clojure_jvm/04_jar_files.md b/_clojure_jvm/04_jar_files.md index a4cf42f..c99c494 100644 --- a/_clojure_jvm/04_jar_files.md +++ b/_clojure_jvm/04_jar_files.md @@ -4,7 +4,7 @@ title: JAR Files --- So far we've seen how Java source files are compiled into binary class files, and how these are located at runtime when the containing directory is placed on the classpath. -This approach works for the single file application developed previously, but most applications consist of many classes, and make use of libraries which themselves define classes +This approach works for the single file application developed previously, but most applications consist of many classes and make use of libraries which themselves define classes and interfaces of their own. Managing and publishing directories of class files would be cumbersome, but fortunately Java defines a standard for packaging related classes into a single file called a Java ARchive, or JAR file. diff --git a/_clojure_jvm/05_dependencies.md b/_clojure_jvm/05_dependencies.md index 5c4f267..6d99e2d 100644 --- a/_clojure_jvm/05_dependencies.md +++ b/_clojure_jvm/05_dependencies.md @@ -57,16 +57,16 @@ Exception in thread "main" java.lang.NoClassDefFoundError: org/json/JSONTokener at enterprisey.JSONMessageSource.fromFile(JSONMessageSource.java:21) ``` -This occurs because when running their application, the other team has not placed `json-java.jar` on the classpath, and this defines +This occurs because, when running their application, the other team has not placed `json-java.jar` on the classpath, and this defines the `org.json.JSONTokener` class. This is not surprising, since given the `libhello.jar` file, there is no way to detect that it depends on `json-java.jar` at runtime. JAR files are just an archive of class files and do not define a mechanism for declaring which other JARs define dependency classes. In addition, the other team has a few other requirements for consuming our library: -* They would prefer not to commit binary files to their source control repository -* They would like to be able to identify the library version from the JAR file name -* They need to be able to identify all dependency JARs so they can fetch them and construct the correct classpath to run their application +* They would prefer not to commit binary files to their source control repository. +* They would like to be able to identify the library version from the JAR file name. +* They need to be able to identify all dependency JARs so they can fetch them and construct the correct classpath to run their application. ## Maven @@ -120,7 +120,7 @@ Therefore, version `0.2.6` of the `data.json` JAR would be fetched to `~/.m2/rep ## Maven library development Now that we understand the basics of Maven we can create a project for our library. First we create a directory for the project and set it up in the -standard Maven layout. As defined in the [super POM](https://maven.apache.org/ref/3.0.4/maven-model-builder/super-pom.html) Java source files go under +standard Maven layout. As defined in the [super POM](https://maven.apache.org/ref/3.0.4/maven-model-builder/super-pom.html), Java source files go under `${project.basedir}/src/main/java`. ``` diff --git a/_clojure_jvm/06_common_issues.md b/_clojure_jvm/06_common_issues.md index 640d172..74a48e3 100644 --- a/_clojure_jvm/06_common_issues.md +++ b/_clojure_jvm/06_common_issues.md @@ -143,7 +143,7 @@ com.picosoft.messaging.db.PostgresMessageSourceFactory com.picosoft.messaging.db.MySqlMessageSourceFactory ``` -when the implementation JAR is on the classpath these classes can be located and loaded via the `ServiceLoader` interface. +when the implementation JAR is on the classpath, these classes can be located and loaded via the `ServiceLoader` interface. Special care must be taken when building an uberjar containing service loader classes. Multiple implementation JARs will define the same `META-INF/services/{service.class}` file. When building uberjars, the default conflict handling behaviour for diff --git a/_clojure_jvm/09_deps.md b/_clojure_jvm/09_deps.md index 93d1ef4..a9f53f9 100644 --- a/_clojure_jvm/09_deps.md +++ b/_clojure_jvm/09_deps.md @@ -7,7 +7,7 @@ So far we've seen how Java files are compiled to class files and collected into so they can be located at program execution time. Tools like Maven are used to manage dependencies and build the required classpath to make them all available. -The existing Java ecosystem is built around publishing JARs to binary repositories, and like Java, Clojure code can be packaged into JAR files, +The existing Java ecosystem is built around publishing JARs to binary repositories, and, like Java, Clojure code can be packaged into JAR files, whether it has been compiled ahead-of-time or is being distributed as source. Many Clojure libraries are published to the [clojars](https://clojars.org/) repository instead of Maven central. However, since directories can be placed on the classpath, and Clojure code can be located and evaluated directly from source, the extra packaging and publish steps are not necessary for developing Clojure libraries and programs. @@ -72,7 +72,7 @@ refered to as the `system` and `user` files. You can find the locations of all t :config-files ["/install/dir/deps.edn", "~/.clojure/deps.edn", "deps.edn"] ``` -If you open the first of these (the system project file) you will see it has default settings for the `:paths` and `:deps` properties +If you open the first of these (the system project file), you will see it has default settings for the `:paths` and `:deps` properties **/install/dir/deps.edn** ```clojure From 78a3daf387b1bec2877827e40547ff68ec5624e1 Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 21:37:25 -0400 Subject: [PATCH 09/14] Capitalization: Clojars --- _clojure_jvm/09_deps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_clojure_jvm/09_deps.md b/_clojure_jvm/09_deps.md index a9f53f9..5c3f916 100644 --- a/_clojure_jvm/09_deps.md +++ b/_clojure_jvm/09_deps.md @@ -8,7 +8,7 @@ so they can be located at program execution time. Tools like Maven are used to m classpath to make them all available. The existing Java ecosystem is built around publishing JARs to binary repositories, and, like Java, Clojure code can be packaged into JAR files, -whether it has been compiled ahead-of-time or is being distributed as source. Many Clojure libraries are published to the [clojars](https://clojars.org/) +whether it has been compiled ahead-of-time or is being distributed as source. Many Clojure libraries are published to the [Clojars](https://clojars.org/) repository instead of Maven central. However, since directories can be placed on the classpath, and Clojure code can be located and evaluated directly from source, the extra packaging and publish steps are not necessary for developing Clojure libraries and programs. From 8b532df69ae01ac60d859b70d0741eb2c9e3646a Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 21:39:40 -0400 Subject: [PATCH 10/14] Capitalization: Maven Central --- _clojure_jvm/05_dependencies.md | 4 ++-- _clojure_jvm/09_deps.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/_clojure_jvm/05_dependencies.md b/_clojure_jvm/05_dependencies.md index 6d99e2d..19bc13d 100644 --- a/_clojure_jvm/05_dependencies.md +++ b/_clojure_jvm/05_dependencies.md @@ -107,7 +107,7 @@ determined by the artifact packaging type - this is usually `jar` for Java artif org/clojure/data.json/0.2.6/data.json-0.2.6.jar -#### Maven central +#### Maven Central Maven defines a single repository `central` in the [super POM](https://maven.apache.org/ref/3.0.4/maven-model-builder/super-pom.html). This is located at http://repo.maven.apache.org/maven2 and it means that Maven projects can reference dependencies published to this repository without any further configuration. @@ -213,7 +213,7 @@ Another option is to build uber JARs using the [Maven shade plugin](https://mave Now the library and application are working locally, it's time to share the library with our colleagues. Our chief architect is convinced it constitutes a key competitive advantage for the company and is unwilling to unleash it on an unsuspecting public. She arranges for a private -Maven repository to be set up, and our CI process to publish there instead of the Maven central repository. We advise any teams wishing to use +Maven repository to be set up, and our CI process to publish there instead of the Maven Central repository. We advise any teams wishing to use it to configure the private repository in their application POM files with the following fragment: ```xml diff --git a/_clojure_jvm/09_deps.md b/_clojure_jvm/09_deps.md index 5c3f916..078f755 100644 --- a/_clojure_jvm/09_deps.md +++ b/_clojure_jvm/09_deps.md @@ -9,7 +9,7 @@ classpath to make them all available. The existing Java ecosystem is built around publishing JARs to binary repositories, and, like Java, Clojure code can be packaged into JAR files, whether it has been compiled ahead-of-time or is being distributed as source. Many Clojure libraries are published to the [Clojars](https://clojars.org/) -repository instead of Maven central. However, since directories can be placed on the classpath, and Clojure code can be located +repository instead of Maven Central. However, since directories can be placed on the classpath, and Clojure code can be located and evaluated directly from source, the extra packaging and publish steps are not necessary for developing Clojure libraries and programs. The [tools.deps](https://github.com/clojure/tools.deps) library is used to declare dependencies from various sources and build the required @@ -36,7 +36,7 @@ Now we can create a basic deps project. The structure of the project is defined {% include code/deps/basic/deps.edn %} ``` -This declares a dependency on the main `clojure` JAR. This JAR is published to Maven central so should be resolved as a Maven dependency. +This declares a dependency on the main `clojure` JAR. This JAR is published to Maven Central so should be resolved as a Maven dependency. This JAR and all of its transitive dependencies should be available on the classpath at runtime. The `src` directory should also be placed on the classpath since that is where our application namespaces are defined. Define the main namespace within this directory: @@ -115,7 +115,7 @@ user=> ## Other dependency sources -The Clojure JARs are published to Maven central so are declared as Maven dependencies in the previous `deps.edn`. Deps supports other dependency +The Clojure JARs are published to Maven Central so are declared as Maven dependencies in the previous `deps.edn`. Deps supports other dependency types such as Git repositories and local directories. We've decided to improve our greeting application by adding an optional command-line flag to show enthusiasm (or not). We also decided to split the From d3648400c4112c0fd15b1ccc1cdbc08288a5917e Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 21:44:51 -0400 Subject: [PATCH 11/14] Capitalization: Java --- _clojure_jvm/02_basic_java.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_clojure_jvm/02_basic_java.md b/_clojure_jvm/02_basic_java.md index 109bd36..0caac31 100644 --- a/_clojure_jvm/02_basic_java.md +++ b/_clojure_jvm/02_basic_java.md @@ -24,7 +24,7 @@ This outputs a `Hello.class` file in the `./test` directory. The `main` method c ## Entry point -Java program execution begins from a named class specified to the java runtime. In the previous example this was the class `test.Hello`. +Java program execution begins from a named class specified to the Java runtime. In the previous example this was the class `test.Hello`. The JVM loads this class and executes its `main` method, which must have the signature `public static void main(String[])` i.e. a static method called `main` which takes a single string array parameter. This array receives all the arguments provided to the `java` command which do not configure the execution of the JVM itself. From f2dc1b481ba5b434de3e9a2527a7ac4a3e2799cc Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 21:51:22 -0400 Subject: [PATCH 12/14] Capitalization: Prefer capital `String` when referring to `java.lang.String` instance --- _clojure_jvm/02_basic_java.md | 2 +- _clojure_jvm/03_class_files.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/_clojure_jvm/02_basic_java.md b/_clojure_jvm/02_basic_java.md index 0caac31..61ca875 100644 --- a/_clojure_jvm/02_basic_java.md +++ b/_clojure_jvm/02_basic_java.md @@ -26,7 +26,7 @@ This outputs a `Hello.class` file in the `./test` directory. The `main` method c Java program execution begins from a named class specified to the Java runtime. In the previous example this was the class `test.Hello`. The JVM loads this class and executes its `main` method, which must have the signature `public static void main(String[])` i.e. a static -method called `main` which takes a single string array parameter. This array receives all the arguments provided to the `java` command which +method called `main` which takes a single String array parameter. This array receives all the arguments provided to the `java` command which do not configure the execution of the JVM itself. ## Loading classes diff --git a/_clojure_jvm/03_class_files.md b/_clojure_jvm/03_class_files.md index 74bd738..6d7410d 100644 --- a/_clojure_jvm/03_class_files.md +++ b/_clojure_jvm/03_class_files.md @@ -119,7 +119,7 @@ Here are the references made by the `test.Hello` class: | #8 | `java/lang/System` | Class | Class which defines the static `out` field | | #7 | `java/lang/System::out` | Field | Reference to the `out` field of the `java.lang.System` class | | #12 | `Ljava/io/PrintStream;` | Type reference | The declared type of the `System.out` field | -| #13 | | String | The string constant "Hello world!" | +| #13 | | String | The String constant "Hello world!" | | #16 | `java/io/PrintStream` | Class | Type defining the `println` method | | #15 | `java/io/PrintStream::println` | Method | The `println` method used to write to the console | | #21 | `test/Hello` | Class | The class defined by this class file. The `this_class` property contains this index into the constant pool | @@ -205,8 +205,8 @@ The flags also indicate the method is `static` and `public`. The [getstatic](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.getstatic) instruction loads a field reference onto the operand stack. The operand of `#7` is the index of the reference to the `System.out` field within the constant pool of the `test.Hello` class. -The string "Hello world!" is pushed onto the operand stack with the [ldc](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.ldc) instruction. The operand of -`#13` is the index of the string within the class constant pool. +The String "Hello world!" is pushed onto the operand stack with the [ldc](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.ldc) instruction. The operand of +`#13` is the index of the String within the class constant pool. -At this point the operand stack contains the arguments to the `java.io.PrintStream.` method - the receiver (the contents of the `System.out` field), and the string to write. The +At this point the operand stack contains the arguments to the `java.io.PrintStream.` method - the receiver (the contents of the `System.out` field), and the String to write. The method is invoked with [invokevirtual](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.invokevirtual) using the method reference in the class constant pool. From e15a90ea44170352a642507ce9d1bbe72f96c703 Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 20:58:19 -0400 Subject: [PATCH 13/14] Capitalization --- _clojure_jvm/03_class_files.md | 2 +- _clojure_jvm/05_dependencies.md | 8 ++++---- _clojure_jvm/06_common_issues.md | 16 ++++++++-------- _clojure_jvm/07_evaluating_clojure.md | 4 ++-- _clojure_jvm/09_deps.md | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/_clojure_jvm/03_class_files.md b/_clojure_jvm/03_class_files.md index 6d7410d..603f8c5 100644 --- a/_clojure_jvm/03_class_files.md +++ b/_clojure_jvm/03_class_files.md @@ -199,7 +199,7 @@ public static void main(java.lang.String[]); line 6: 8 ``` -As indicated by the descriptor, The `main` method defines a single `String[]` parameter and has a return type of `void`. +As indicated by the descriptor, the `main` method defines a single `String[]` parameter and has a return type of `void`. The flags also indicate the method is `static` and `public`. The [getstatic](https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-6.html#jvms-6.5.getstatic) instruction loads a field reference onto the operand stack. The operand of `#7` is the index of the reference to the diff --git a/_clojure_jvm/05_dependencies.md b/_clojure_jvm/05_dependencies.md index 19bc13d..1780e12 100644 --- a/_clojure_jvm/05_dependencies.md +++ b/_clojure_jvm/05_dependencies.md @@ -19,19 +19,19 @@ We fetch the JSON library JAR locally and begin work: > curl -L -o json-java.jar https://search.maven.org/remotecontent?filepath=org/json/json/20230618/json-20230618.jar -after a frenetic caffeine-fueled 10-hour coding session, we finally have our new message source implementation: +After a frenetic caffeine-fueled 10-hour coding session, we finally have our new message source implementation: __src/libhello/JSONMessageSource.java__ ``` {% include code/java_dependencies/JSONMessageSource.java %} ``` -as before, we compile the library, updating the build classpath to include the dependency JAR: +As before, we compile the library, updating the build classpath to include the dependency JAR: > javac -cp json-java.jar -d classes/libhello src/libhello/*.java > jar --create --file libhello.jar -C classes/libhello . -we write a small test application and data file for the new source +We write a small test application and data file for the new source __EchoJSON.java__ ```java @@ -48,7 +48,7 @@ and run it to check the input messages are displayed as expected: > javac -cp json-java.jar:libhello.jar -d app EchoJSON.java > java -cp json-java.jar:libhello.jar:app test.EchoJSON messages.json -satisfied with another job well done, we email the updated JAR to the chief scrum master of the originating team for the request. +Satisfied with another job well done, we email the updated JAR to the chief scrum master of the originating team for the request. Shortly after, we receive a response complaining that when running their application, they receive the following error: diff --git a/_clojure_jvm/06_common_issues.md b/_clojure_jvm/06_common_issues.md index 74a48e3..b718fbc 100644 --- a/_clojure_jvm/06_common_issues.md +++ b/_clojure_jvm/06_common_issues.md @@ -44,7 +44,7 @@ then get the properties of the running process jinfo -sysprops -again the `java.class.path` property contains the classpath of the process. +Again the `java.class.path` property contains the classpath of the process. ## UnsupportedClassVersionError @@ -61,21 +61,21 @@ This error usually occurs when an application is compiled against one version of {% include code/common_issues/version1/Greeter.java %} ``` -after some time, a new version of this class is created with a new method: +After some time, a new version of this class is created with a new method: **version2/Greeter.java** ```java {% include code/common_issues/version2/Greeter.java %} ``` -an application is created which uses the newer version: +An application is created which uses the newer version: **GreetApp.java** ```java {% include code/common_issues/GreetApp.java %} ``` -compiling both class versions and the application against the newer version +Compiling both class versions and the application against the newer version ``` pushd version1 && javac Greeter.java && popd @@ -83,11 +83,11 @@ pushd version2 && javac Greeter.java && popd javac -cp .:version2 GreetApp.java ``` -running the application with the new version on the classpath works as expected: +Running the application with the new version on the classpath works as expected: java -cp .:version2 GreetApp -running with the old version on the classpath results in an error: +Running with the old version on the classpath results in an error: ``` > java -cp .:version1 GreetApp @@ -135,7 +135,7 @@ public class PostgresMessageSourceFactory implements MessageSourceFactory { ... public class MySqlMessageSourceFactory implements MessageSourceFactory { ... } ``` -these would then be listed as implementation classes within the corresponding interface service file: +These would then be listed as implementation classes within the corresponding interface service file: **META-INF/services/com.picosoft.messaging.MessageSourceFactory** ``` @@ -143,7 +143,7 @@ com.picosoft.messaging.db.PostgresMessageSourceFactory com.picosoft.messaging.db.MySqlMessageSourceFactory ``` -when the implementation JAR is on the classpath, these classes can be located and loaded via the `ServiceLoader` interface. +When the implementation JAR is on the classpath, these classes can be located and loaded via the `ServiceLoader` interface. Special care must be taken when building an uberjar containing service loader classes. Multiple implementation JARs will define the same `META-INF/services/{service.class}` file. When building uberjars, the default conflict handling behaviour for diff --git a/_clojure_jvm/07_evaluating_clojure.md b/_clojure_jvm/07_evaluating_clojure.md index d964597..84aac54 100644 --- a/_clojure_jvm/07_evaluating_clojure.md +++ b/_clojure_jvm/07_evaluating_clojure.md @@ -15,7 +15,7 @@ curl -LO https://repo1.maven.org/maven2/org/clojure/spec.alpha/0.3.218/spec.alph curl -LO https://repo1.maven.org/maven2/org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.jar ``` -this should leave you with `clojure-1.11.1.jar`, `spec.alpha-0.3.218.jar` and `core.specs.alpha-0.2.62.jar` in the current directory. +This should leave you with `clojure-1.11.1.jar`, `spec.alpha-0.3.218.jar` and `core.specs.alpha-0.2.62.jar` in the current directory. ## clojure.main @@ -70,7 +70,7 @@ into JAR files for easier distribution. As with `.class` files this can be done > jar --create --file lib-hello.jar -C src . -this packages the contents of the `src` directory into `lib-hello.jar` and this can be placed on the classpath as before: +This packages the contents of the `src` directory into `lib-hello.jar` and this can be placed on the classpath as before: ``` > java -cp clojure-1.11.1.jar:core.specs.alpha-0.2.62.jar:spec.alpha-0.3.218.jar:lib-hello.jar clojure.main -m org.picosoft.lib-hello.core diff --git a/_clojure_jvm/09_deps.md b/_clojure_jvm/09_deps.md index 078f755..749971c 100644 --- a/_clojure_jvm/09_deps.md +++ b/_clojure_jvm/09_deps.md @@ -52,14 +52,14 @@ The `clojure` CLI allows us to invoke `clojure.main` with the project classpath Hello everyone! ``` -you can display the computed classpath with +You can display the computed classpath with ``` > clojure -Spath src:~/.m2/repository/org/clojure/clojure/1.11.1/clojure-1.11.1.jar:... ``` -as expected, it contains our `src` directory and the JAR files for the Clojure JAR and all its dependencies. +As expected, it contains our `src` directory and the JAR files for the Clojure JAR and all its dependencies. ### System and user deps @@ -104,7 +104,7 @@ You can define a `dev` alias within the project `deps.edn` file {% include code/deps/aliases/deps.edn %} ``` -then supply it when starting the REPL: +Then supply it when starting the REPL: ``` > clojure -A:dev From b1cb050a5c0a16fe1084dd1bca4dd60f53f7528f Mon Sep 17 00:00:00 2001 From: Sam Umbach Date: Mon, 24 Mar 2025 20:59:44 -0400 Subject: [PATCH 14/14] Minor formatting changes --- _clojure_jvm/03_class_files.md | 2 +- _clojure_jvm/04_jar_files.md | 2 +- _clojure_jvm/05_dependencies.md | 2 +- _clojure_jvm/08_compiling_clojure.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/_clojure_jvm/03_class_files.md b/_clojure_jvm/03_class_files.md index 603f8c5..6cba6db 100644 --- a/_clojure_jvm/03_class_files.md +++ b/_clojure_jvm/03_class_files.md @@ -93,7 +93,7 @@ field descriptors denotes one of these three possibilities. The primitive types are `byte`, `char`, `double`, `float`, `int`, `long`, `short` and `boolean`, denoted as `B`, `C`, `D`, `F`, `I`, `J`, `S` and `Z` respectively within the field descriptor grammar. For example, a descriptor of `J` indicates a field of the primitive `long` type. -Object types are defined with the literal `L` followed by the binary name of the class type followed by the literal ';'. For example a field of type `java.lang.Thead` has a descriptor of `Ljava/lang/Thread;`. +Object types are defined with the literal `L` followed by the binary name of the class type followed by the literal `;`. For example a field of type `java.lang.Thead` has a descriptor of `Ljava/lang/Thread;`. Array types are defined with the literal `[` followed by the element type. So an array of `java.lang.Thread` references has a descriptor of `[Ljava/lang/Thread;` and an array of primitive `int` values has a descriptor of `[I`. diff --git a/_clojure_jvm/04_jar_files.md b/_clojure_jvm/04_jar_files.md index c99c494..29d554d 100644 --- a/_clojure_jvm/04_jar_files.md +++ b/_clojure_jvm/04_jar_files.md @@ -59,7 +59,7 @@ this creates a `libhello.jar` file in the current directory. We can list the con jar --list --file libhello.jar This shows the archive contains the `.class` files as their expected locations on the classpath, along with a `META-INF/MANIFEST.MF` file. -This file is called the `_manifest` file and is described [below](#manifest-files). +This file is called the _manifest_ file and is described [below](#manifest-files). ``` {% include code/jar/jar_list %} diff --git a/_clojure_jvm/05_dependencies.md b/_clojure_jvm/05_dependencies.md index 1780e12..c56ecd8 100644 --- a/_clojure_jvm/05_dependencies.md +++ b/_clojure_jvm/05_dependencies.md @@ -109,7 +109,7 @@ determined by the artifact packaging type - this is usually `jar` for Java artif #### Maven Central -Maven defines a single repository `central` in the [super POM](https://maven.apache.org/ref/3.0.4/maven-model-builder/super-pom.html). This is located at http://repo.maven.apache.org/maven2 and it means that Maven +Maven defines a single repository `central` in the [super POM](https://maven.apache.org/ref/3.0.4/maven-model-builder/super-pom.html). This is located at `http://repo.maven.apache.org/maven2` and it means that Maven projects can reference dependencies published to this repository without any further configuration. #### Local repositories diff --git a/_clojure_jvm/08_compiling_clojure.md b/_clojure_jvm/08_compiling_clojure.md index e31bb04..1cf48f3 100644 --- a/_clojure_jvm/08_compiling_clojure.md +++ b/_clojure_jvm/08_compiling_clojure.md @@ -49,7 +49,7 @@ Hello everyone! The `ns` macro used to define namespaces supports an optional `:gen-class` option to compile a class for the namespace. It supports all the options of [gen-class](https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/gen-class) -and by default uses the namespace name as the class name, sets `:main` to `true`, and sets the prefix to '-'. This means +and by default uses the namespace name as the class name, sets `:main` to `true`, and sets the prefix to `-`. This means a `-main` function is expected to exist which corresponds to the `main` method of the generated class. This allows us to write straightforward Clojure code which is compiled into a Java entrypoint without having to invoke it via