Sorry

This feed does not validate.

In addition, interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://unenc.frostillic.us/f.nsf/feed.xml

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://frostillic.us/blog" version="2.0"><channel><title>frostillic.us</title><link>https://frostillic.us/blog</link><description>frostillic.us Blog</description><image><link>https://frostillic.us/blog</link><title>frostillic.us</title><url>https://frostillic.us/blog/img/icon.png</url></image><item><title>Simplifying the Maven Build of the NSF File Server Project</title><link>https://frostillic.us/blog/posts/2024/4/10/simplifying-the-maven-build-of-the-nsf-file-server-project</link><content:encoded>&lt;p&gt;When working on &lt;a href="https://www.openntf.org/main.nsf/project.xsp?r=project/640CDDAE57EAAC6A86258AF8005DEF10"&gt;NSF File Server&lt;/a&gt; project that I &lt;a href="/blog/posts/2024/4/7/nsf-file-server-2-0"&gt;talked about the other day&lt;/a&gt;, I took a slightly-different tack as far as building it than I did in the past, and I think it's worth going over some of that in case it's useful for others.&lt;/p&gt;
  2. &lt;h3&gt;Initial Version&lt;/h3&gt;
  3. &lt;p&gt;The first version of this project was a non-OSGi WAR file meant to be deployed to an app server like Liberty, not to Domino's OSGi stack, and so it's never involved &lt;a href="/blog/posts/2015/3/14/F952B6D5F7BAE6C685257E0800632220"&gt;Tycho&lt;/a&gt;. This made it mostly simpler, since its various dependencies are normal Maven dependencies and so I didn't have to worry about any of the annoying hoops.&lt;/p&gt;
  4. &lt;p&gt;However, it &lt;em&gt;did&lt;/em&gt; have some native Domino dependencies: Notes.jar and the NAPI. These would need to be included as Maven dependencies and brought into the final WAR. The way I handled this was using the &lt;a href="https://github.com/OpenNTF/generate-domino-update-site/"&gt;generate-domino-update-site project&lt;/a&gt;, which lets you first generate a p2 site in the style of the &lt;a href="https://www.openntf.org/main.nsf/project.xsp?r=project/IBM%20Domino%20Update%20Site%20for%20Build%20Management"&gt;painfully outdated IBM-provided update site&lt;/a&gt; and then, if desired, turn that p2 site into more-normal Maven artifacts.&lt;/p&gt;
  5. &lt;p&gt;When I eventually switched from targeting a WAR file to having it run on Domino, I used the same dependency structure. The Domino version runs as an &lt;a href="https://github.com/IKSG/nsf-file-server/blob/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino/nsf-file-server-httpservice/src/main/java/org/openntf/nsffile/httpservice/SFTPService.java"&gt;&lt;code&gt;HttpService&lt;/code&gt; implementation&lt;/a&gt;, and so I pointed at the Mavenized version of the com.ibm.xsp.bootstrap and com.ibm.domino.xsp.adapter bundles.&lt;/p&gt;
  6. &lt;p&gt;Then, I used the &lt;a href="https://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html"&gt;maven-bundle-plugin&lt;/a&gt;, which fits the job of taking an otherwise-normal Maven project and making it work in OSGi environments (mostly). The way that plugin works is that you specify a lot of your MANIFEST.MF rules &lt;a href="https://github.com/IKSG/nsf-file-server/blob/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino/nsf-file-server-httpservice/pom.xml"&gt;in the pom.xml&lt;/a&gt;:&lt;/p&gt;
  7. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt; 1
  8. 2
  9. 3
  10. 4
  11. 5
  12. 6
  13. 7
  14. 8
  15. 9
  16. 10
  17. 11
  18. 12
  19. 13
  20. 14
  21. 15
  22. 16
  23. 17
  24. 18
  25. 19
  26. 20
  27. 21
  28. 22
  29. 23
  30. 24
  31. 25
  32. 26
  33. 27
  34. 28&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;&lt;span style="color: #007700"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
  35. &lt;span style="color: #007700"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.felix&lt;span style="color: #007700"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  36. &lt;span style="color: #007700"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-bundle-plugin&lt;span style="color: #007700"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  37. &lt;span style="color: #007700"&gt;&amp;lt;extensions&amp;gt;&lt;/span&gt;true&lt;span style="color: #007700"&gt;&amp;lt;/extensions&amp;gt;&lt;/span&gt;
  38. &lt;span style="color: #007700"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  39. &lt;span style="color: #007700"&gt;&amp;lt;instructions&amp;gt;&lt;/span&gt;
  40. &lt;span style="color: #007700"&gt;&amp;lt;Bundle-SymbolicName&amp;gt;&lt;/span&gt;org.openntf.nsffile.httpservice;singleton:=true&lt;span style="color: #007700"&gt;&amp;lt;/Bundle-SymbolicName&amp;gt;&lt;/span&gt;
  41. &lt;span style="color: #007700"&gt;&amp;lt;Bundle-RequiredExecutionEnvironment&amp;gt;&lt;/span&gt;JavaSE-1.8&lt;span style="color: #007700"&gt;&amp;lt;/Bundle-RequiredExecutionEnvironment&amp;gt;&lt;/span&gt;
  42. &lt;span style="color: #007700"&gt;&amp;lt;Export-Package/&amp;gt;&lt;/span&gt;
  43. &lt;span style="color: #007700"&gt;&amp;lt;Require-Bundle&amp;gt;&lt;/span&gt;
  44. com.ibm.domino.xsp.adapter,
  45. com.ibm.commons,
  46. com.ibm.commons.xml
  47. &lt;span style="color: #007700"&gt;&amp;lt;/Require-Bundle&amp;gt;&lt;/span&gt;
  48. &lt;span style="color: #007700"&gt;&amp;lt;Import-Package&amp;gt;&lt;/span&gt;
  49. javax.servlet,
  50. javax.servlet.http
  51. &lt;span style="color: #007700"&gt;&amp;lt;/Import-Package&amp;gt;&lt;/span&gt;
  52. &lt;span style="color: #007700"&gt;&amp;lt;Embed-Dependency&amp;gt;&lt;/span&gt;*;scope=compile&lt;span style="color: #007700"&gt;&amp;lt;/Embed-Dependency&amp;gt;&lt;/span&gt;
  53. &lt;span style="color: #007700"&gt;&amp;lt;Embed-Transitive&amp;gt;&lt;/span&gt;true&lt;span style="color: #007700"&gt;&amp;lt;/Embed-Transitive&amp;gt;&lt;/span&gt;
  54. &lt;span style="color: #007700"&gt;&amp;lt;Embed-Directory&amp;gt;&lt;/span&gt;lib&lt;span style="color: #007700"&gt;&amp;lt;/Embed-Directory&amp;gt;&lt;/span&gt;
  55. &lt;span style="color: #007700"&gt;&amp;lt;_removeheaders&amp;gt;&lt;/span&gt;Require-Capability&lt;span style="color: #007700"&gt;&amp;lt;/_removeheaders&amp;gt;&lt;/span&gt;
  56. &lt;span style="color: #007700"&gt;&amp;lt;_snapshot&amp;gt;&lt;/span&gt;${osgi.qualifier}&lt;span style="color: #007700"&gt;&amp;lt;/_snapshot&amp;gt;&lt;/span&gt;
  57. &lt;span style="color: #007700"&gt;&amp;lt;/instructions&amp;gt;&lt;/span&gt;
  58. &lt;span style="color: #007700"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
  59. &lt;span style="color: #007700"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
  60. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  61. &lt;/div&gt;
  62. &lt;p&gt;The first couple are one-for-one matches to what you'd have in the MANIFEST.MF, but things get weird once you get to the &amp;quot;Embed-*&amp;quot; ones.&lt;/p&gt;
  63. &lt;p&gt;The &lt;code&gt;Embed-Dependency&lt;/code&gt; instruction is a potent one: you give it a description of what dependencies you want embedded in your OSGi bundle (in this case, all my non-&lt;code&gt;provided&lt;/code&gt; dependencies), and then it does the job of copying them into your final bundle JAR. You can do this other ways - copying them manually, using the Maven Dependency Plugin, or others - but this handles all your transitive stuff nicely for you, thanks to &lt;code&gt;Embed-Transitive&lt;/code&gt;. I use &lt;code&gt;Embed-Directory&lt;/code&gt; here just for cleanliness - the result is functionally the same without it.&lt;/p&gt;
  64. &lt;p&gt;The final bits are just for cleanliness: I remove &lt;code&gt;Require-Capability&lt;/code&gt; to avoid some trouble I had with older Domino versions, and then I set what the snapshot value will be, which ends up being the current build time.&lt;/p&gt;
  65. &lt;p&gt;With this, I end up with a single OSGi bundle with everything in it. This works well for this sort of project - with something to be used in Designer, I prefer to make a big pool of distinct OSGi bundles to make it so that you can look up the source properly, but something server-only like this doesn't need that.&lt;/p&gt;
  66. &lt;h3&gt;2.0 Version&lt;/h3&gt;
  67. &lt;p&gt;In this new version, the switch to JNX meant that I was tantalizingly close to not having to do any weird dependency stuff: JNX is distributed in Maven Central, so I didn't need to have the weird locally-built stuff like I did for Notes.jar and the NAPI.&lt;/p&gt;
  68. &lt;p&gt;However, that wasn't everything: there are still the &amp;quot;bootstrap&amp;quot; bundles containing the &lt;code&gt;HttpService&lt;/code&gt; superclass and related classes. While I don't need to distribute those anywhere, they're still required to compile the classes - no amount of non-verified text files or the like will get around that.&lt;/p&gt;
  69. &lt;p&gt;I came up with another way, though. Java only needs classes that &lt;em&gt;look like&lt;/em&gt; those to compile, and then the compiled class of mine will be the same regardless of anything else. This is critical: I don't actually need any implementation code, and that's the part I can't redistribute. So I made two little Maven modules: &lt;a href="https://github.com/IKSG/nsf-file-server/tree/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino/com.ibm.xsp.bootstrap.shim"&gt;&lt;code&gt;com.ibm.xsp.bootstrap.shim&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/IKSG/nsf-file-server/tree/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino/com.ibm.domino.xsp.adapter.shim"&gt;&lt;code&gt;com.ibm.domino.xsp.adapter.shim&lt;/code&gt;&lt;/a&gt;. These modules contain just a handful of classes, and of those classes only the methods actually referenced by my code. Since these modules will then be marked as &amp;quot;provided&amp;quot;, they won't be bundled into the final JAR.&lt;/p&gt;
  70. &lt;p&gt;This squared the circle nicely, where now I can compile the Java side without any weird pre-requisites. Admittedly, the two NSFs in the &lt;a href="https://github.com/IKSG/nsf-file-server/tree/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino"&gt;module set&lt;/a&gt; still require an NSF ODP Tooling environment, which is a whole other ball of wax, but it's a step in the right direction.&lt;/p&gt;
  71. &lt;h3&gt;Other Uses&lt;/h3&gt;
  72. &lt;p&gt;This technique can be used in other similar projects when you only need a few classes from the XPages stack. For example, if your goal is to just wrap a third-party library and provide it to XPages in Designer, you could probably do this by making a stub implementation of &lt;code&gt;XspLibrary&lt;/code&gt; and related classes, and skip the whole generate-domino-update-site step. The more you use from the stack, the less practical this is - for example, the XPages Jakarta EE project reaches into all sorts of crap, and so I can't really do this there. For this, though, it works nicely.&lt;/p&gt;
  73. </content:encoded><guid>9EC872A4D3084FE890BB085B03FDA977</guid><dc:creator>Jesse Gallagher</dc:creator><dc:date>2024-04-10T21:02:09.161Z</dc:date><pubDate>Wed, 10 Apr 2024 21:02:09 GMT</pubDate><description>&lt;p&gt;When working on &lt;a href="https://www.openntf.org/main.nsf/project.xsp?r=project/640CDDAE57EAAC6A86258AF8005DEF10"&gt;NSF File Server&lt;/a&gt; project that I &lt;a href="/blog/posts/2024/4/7/nsf-file-server-2-0"&gt;talked about the other day&lt;/a&gt;, I took a slightly-different tack as far as building it than I did in the past, and I think it's worth going over some of that in case it's useful for others.&lt;/p&gt;
  74. &lt;h3&gt;Initial Version&lt;/h3&gt;
  75. &lt;p&gt;The first version of this project was a non-OSGi WAR file meant to be deployed to an app server like Liberty, not to Domino's OSGi stack, and so it's never involved &lt;a href="/blog/posts/2015/3/14/F952B6D5F7BAE6C685257E0800632220"&gt;Tycho&lt;/a&gt;. This made it mostly simpler, since its various dependencies are normal Maven dependencies and so I didn't have to worry about any of the annoying hoops.&lt;/p&gt;
  76. &lt;p&gt;However, it &lt;em&gt;did&lt;/em&gt; have some native Domino dependencies: Notes.jar and the NAPI. These would need to be included as Maven dependencies and brought into the final WAR. The way I handled this was using the &lt;a href="https://github.com/OpenNTF/generate-domino-update-site/"&gt;generate-domino-update-site project&lt;/a&gt;, which lets you first generate a p2 site in the style of the &lt;a href="https://www.openntf.org/main.nsf/project.xsp?r=project/IBM%20Domino%20Update%20Site%20for%20Build%20Management"&gt;painfully outdated IBM-provided update site&lt;/a&gt; and then, if desired, turn that p2 site into more-normal Maven artifacts.&lt;/p&gt;
  77. &lt;p&gt;When I eventually switched from targeting a WAR file to having it run on Domino, I used the same dependency structure. The Domino version runs as an &lt;a href="https://github.com/IKSG/nsf-file-server/blob/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino/nsf-file-server-httpservice/src/main/java/org/openntf/nsffile/httpservice/SFTPService.java"&gt;&lt;code&gt;HttpService&lt;/code&gt; implementation&lt;/a&gt;, and so I pointed at the Mavenized version of the com.ibm.xsp.bootstrap and com.ibm.domino.xsp.adapter bundles.&lt;/p&gt;
  78. &lt;p&gt;Then, I used the &lt;a href="https://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html"&gt;maven-bundle-plugin&lt;/a&gt;, which fits the job of taking an otherwise-normal Maven project and making it work in OSGi environments (mostly). The way that plugin works is that you specify a lot of your MANIFEST.MF rules &lt;a href="https://github.com/IKSG/nsf-file-server/blob/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino/nsf-file-server-httpservice/pom.xml"&gt;in the pom.xml&lt;/a&gt;:&lt;/p&gt;
  79. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt; 1
  80. 2
  81. 3
  82. 4
  83. 5
  84. 6
  85. 7
  86. 8
  87. 9
  88. 10
  89. 11
  90. 12
  91. 13
  92. 14
  93. 15
  94. 16
  95. 17
  96. 18
  97. 19
  98. 20
  99. 21
  100. 22
  101. 23
  102. 24
  103. 25
  104. 26
  105. 27
  106. 28&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;&lt;span style="color: #007700"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
  107. &lt;span style="color: #007700"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.felix&lt;span style="color: #007700"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  108. &lt;span style="color: #007700"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-bundle-plugin&lt;span style="color: #007700"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  109. &lt;span style="color: #007700"&gt;&amp;lt;extensions&amp;gt;&lt;/span&gt;true&lt;span style="color: #007700"&gt;&amp;lt;/extensions&amp;gt;&lt;/span&gt;
  110. &lt;span style="color: #007700"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  111. &lt;span style="color: #007700"&gt;&amp;lt;instructions&amp;gt;&lt;/span&gt;
  112. &lt;span style="color: #007700"&gt;&amp;lt;Bundle-SymbolicName&amp;gt;&lt;/span&gt;org.openntf.nsffile.httpservice;singleton:=true&lt;span style="color: #007700"&gt;&amp;lt;/Bundle-SymbolicName&amp;gt;&lt;/span&gt;
  113. &lt;span style="color: #007700"&gt;&amp;lt;Bundle-RequiredExecutionEnvironment&amp;gt;&lt;/span&gt;JavaSE-1.8&lt;span style="color: #007700"&gt;&amp;lt;/Bundle-RequiredExecutionEnvironment&amp;gt;&lt;/span&gt;
  114. &lt;span style="color: #007700"&gt;&amp;lt;Export-Package/&amp;gt;&lt;/span&gt;
  115. &lt;span style="color: #007700"&gt;&amp;lt;Require-Bundle&amp;gt;&lt;/span&gt;
  116. com.ibm.domino.xsp.adapter,
  117. com.ibm.commons,
  118. com.ibm.commons.xml
  119. &lt;span style="color: #007700"&gt;&amp;lt;/Require-Bundle&amp;gt;&lt;/span&gt;
  120. &lt;span style="color: #007700"&gt;&amp;lt;Import-Package&amp;gt;&lt;/span&gt;
  121. javax.servlet,
  122. javax.servlet.http
  123. &lt;span style="color: #007700"&gt;&amp;lt;/Import-Package&amp;gt;&lt;/span&gt;
  124. &lt;span style="color: #007700"&gt;&amp;lt;Embed-Dependency&amp;gt;&lt;/span&gt;*;scope=compile&lt;span style="color: #007700"&gt;&amp;lt;/Embed-Dependency&amp;gt;&lt;/span&gt;
  125. &lt;span style="color: #007700"&gt;&amp;lt;Embed-Transitive&amp;gt;&lt;/span&gt;true&lt;span style="color: #007700"&gt;&amp;lt;/Embed-Transitive&amp;gt;&lt;/span&gt;
  126. &lt;span style="color: #007700"&gt;&amp;lt;Embed-Directory&amp;gt;&lt;/span&gt;lib&lt;span style="color: #007700"&gt;&amp;lt;/Embed-Directory&amp;gt;&lt;/span&gt;
  127. &lt;span style="color: #007700"&gt;&amp;lt;_removeheaders&amp;gt;&lt;/span&gt;Require-Capability&lt;span style="color: #007700"&gt;&amp;lt;/_removeheaders&amp;gt;&lt;/span&gt;
  128. &lt;span style="color: #007700"&gt;&amp;lt;_snapshot&amp;gt;&lt;/span&gt;${osgi.qualifier}&lt;span style="color: #007700"&gt;&amp;lt;/_snapshot&amp;gt;&lt;/span&gt;
  129. &lt;span style="color: #007700"&gt;&amp;lt;/instructions&amp;gt;&lt;/span&gt;
  130. &lt;span style="color: #007700"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
  131. &lt;span style="color: #007700"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
  132. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  133. &lt;/div&gt;
  134. &lt;p&gt;The first couple are one-for-one matches to what you'd have in the MANIFEST.MF, but things get weird once you get to the &amp;quot;Embed-*&amp;quot; ones.&lt;/p&gt;
  135. &lt;p&gt;The &lt;code&gt;Embed-Dependency&lt;/code&gt; instruction is a potent one: you give it a description of what dependencies you want embedded in your OSGi bundle (in this case, all my non-&lt;code&gt;provided&lt;/code&gt; dependencies), and then it does the job of copying them into your final bundle JAR. You can do this other ways - copying them manually, using the Maven Dependency Plugin, or others - but this handles all your transitive stuff nicely for you, thanks to &lt;code&gt;Embed-Transitive&lt;/code&gt;. I use &lt;code&gt;Embed-Directory&lt;/code&gt; here just for cleanliness - the result is functionally the same without it.&lt;/p&gt;
  136. &lt;p&gt;The final bits are just for cleanliness: I remove &lt;code&gt;Require-Capability&lt;/code&gt; to avoid some trouble I had with older Domino versions, and then I set what the snapshot value will be, which ends up being the current build time.&lt;/p&gt;
  137. &lt;p&gt;With this, I end up with a single OSGi bundle with everything in it. This works well for this sort of project - with something to be used in Designer, I prefer to make a big pool of distinct OSGi bundles to make it so that you can look up the source properly, but something server-only like this doesn't need that.&lt;/p&gt;
  138. &lt;h3&gt;2.0 Version&lt;/h3&gt;
  139. &lt;p&gt;In this new version, the switch to JNX meant that I was tantalizingly close to not having to do any weird dependency stuff: JNX is distributed in Maven Central, so I didn't need to have the weird locally-built stuff like I did for Notes.jar and the NAPI.&lt;/p&gt;
  140. &lt;p&gt;However, that wasn't everything: there are still the &amp;quot;bootstrap&amp;quot; bundles containing the &lt;code&gt;HttpService&lt;/code&gt; superclass and related classes. While I don't need to distribute those anywhere, they're still required to compile the classes - no amount of non-verified text files or the like will get around that.&lt;/p&gt;
  141. &lt;p&gt;I came up with another way, though. Java only needs classes that &lt;em&gt;look like&lt;/em&gt; those to compile, and then the compiled class of mine will be the same regardless of anything else. This is critical: I don't actually need any implementation code, and that's the part I can't redistribute. So I made two little Maven modules: &lt;a href="https://github.com/IKSG/nsf-file-server/tree/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino/com.ibm.xsp.bootstrap.shim"&gt;&lt;code&gt;com.ibm.xsp.bootstrap.shim&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/IKSG/nsf-file-server/tree/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino/com.ibm.domino.xsp.adapter.shim"&gt;&lt;code&gt;com.ibm.domino.xsp.adapter.shim&lt;/code&gt;&lt;/a&gt;. These modules contain just a handful of classes, and of those classes only the methods actually referenced by my code. Since these modules will then be marked as &amp;quot;provided&amp;quot;, they won't be bundled into the final JAR.&lt;/p&gt;
  142. &lt;p&gt;This squared the circle nicely, where now I can compile the Java side without any weird pre-requisites. Admittedly, the two NSFs in the &lt;a href="https://github.com/IKSG/nsf-file-server/tree/995686e0f42bf4771d4d61d1a35f01115134f13c/nsf-file-server/domino"&gt;module set&lt;/a&gt; still require an NSF ODP Tooling environment, which is a whole other ball of wax, but it's a step in the right direction.&lt;/p&gt;
  143. &lt;h3&gt;Other Uses&lt;/h3&gt;
  144. &lt;p&gt;This technique can be used in other similar projects when you only need a few classes from the XPages stack. For example, if your goal is to just wrap a third-party library and provide it to XPages in Designer, you could probably do this by making a stub implementation of &lt;code&gt;XspLibrary&lt;/code&gt; and related classes, and skip the whole generate-domino-update-site step. The more you use from the stack, the less practical this is - for example, the XPages Jakarta EE project reaches into all sorts of crap, and so I can't really do this there. For this, though, it works nicely.&lt;/p&gt;
  145. </description></item><item><title>NSF File Server 2.0</title><link>https://frostillic.us/blog/posts/2024/4/7/nsf-file-server-2-0</link><content:encoded>&lt;p&gt;A few years ago, I made a little project that &lt;a href="/blog/posts/2019/11/5/another-side-project-nsf-sftp-file-store"&gt;hosts an SFTP server that stores documents in an NSF&lt;/a&gt;. I've used it here and there since then - as in the original post, I stashed some company docs in it to have them nicely synced among our Domino servers, and I've also had cases where clients use it to, for example, provide a way for their vendors to upload files in a standard way.&lt;/p&gt;
  146. &lt;p&gt;The other week, I decided to dive back into it to add some capabilities I'd wanted for a while, and the result is &lt;a href="https://www.openntf.org/main.nsf/project.xsp?r=project/640CDDAE57EAAC6A86258AF8005DEF10/releases/95FED8031FC3659286258AF8005E1963"&gt;version 2.0.0&lt;/a&gt;. This version is a significant revamp that adds quite a bit.&lt;/p&gt;
  147. &lt;h3&gt;Multiple Mounts&lt;/h3&gt;
  148. &lt;p&gt;The first limitation I wanted to improve was the fact that the first version was restricted to a single NSF. That works fine in the basic case, but I wanted to start doing things like storing server config backups in there, and wouldn't necessarily want them in the same NSF as, say, company contracts, credentials, and secrets.&lt;/p&gt;
  149. &lt;p&gt;The way I went about this was to make it so that the new configuration NSF has a &amp;quot;Mounts&amp;quot; view that lets you specify a path in a conceptual top level of the filesystem that would then point to a target NSF. This allows the admin to do things like have separate ACLs for different mounts - since the client will act as an authenticated Domino user, these will be properly obeyed, and a user won't be able to access documents they don't have rights to.&lt;/p&gt;
  150. &lt;p&gt;Additionally, I could configure it so that not all mounts are present on all servers, which will come into play particularly with the next feature.&lt;/p&gt;
  151. &lt;img src="media/C8BDF19A6CCD429FBA0EE561FEF890B3/sftp-mounts.png" alt="Screenshot of the Mounts view in the file server config NSF" title="Screenshot of the Mounts view in the file server config NSF" border="0" width="685" height="209" /&gt;
  152. &lt;h3&gt;New Filesystem Types&lt;/h3&gt;
  153. &lt;p&gt;Once I had a composite top-level filesystem, I realized that it wouldn't be terribly difficult to allow more filesystem types than the original NSF file store I made. That filesystem is built using the &lt;a href="https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/filesystemprovider.html"&gt;NIO File System Provider&lt;/a&gt; framework added in Java 7, and that system is designed to be pretty extensible. By default, Java comes with a few providers: the normal local filesystem as well as one that can treat a ZIP or JAR as a contained filesystem itself. These are accessed in a generic way, where you specify a URI and a map of &amp;quot;env&amp;quot; properties that are provider-specific.&lt;/p&gt;
  154. &lt;p&gt;For example, the &lt;a href="https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.html"&gt;ZIP filesystem&lt;/a&gt; takes a URI in the form of &amp;quot;jar:file:/some/path/to/file.zip&amp;quot; and an environment map configuring whether the filesystem should be created if it doesn't already exist in memory and what encoding to use for filenames (very important if you have Unicode characters in there).&lt;/p&gt;
  155. &lt;p&gt;So I added ways to configure a mount to the local server filesystem (similar to what the &lt;a href="https://www.openntf.org/internal/home.nsf/project.xsp?action=openDocument&amp;amp;name=Mindoo%20FTP%20Server"&gt;Mindoo FTP Server&lt;/a&gt; does) and then a generic configuration for any installed provider. It's probably uncommon that you will have a custom File System Provider implementation in your Java classpath, but hey, maybe you do, and I want to allow that to work.&lt;/p&gt;
  156. &lt;p&gt;I also added an extension point to the project itself that allows adding new providers via plugin.xml files in OSGi, and I can think of a couple other projects that may use this, like the NSF ODP Tooling.&lt;/p&gt;
  157. &lt;h3&gt;WebContent Filesystem&lt;/h3&gt;
  158. &lt;p&gt;Beyond adding the JVM-provided systems, I wrote another new filesystem type, one that provides access to the conceptual &amp;quot;WebContent&amp;quot; directory presented in Package Explorer in Designer:&lt;/p&gt;
  159. &lt;img src="media/ECC241D1172F47408B85B8DF2EAAF777/sftp-webcontent.png" alt="Screenshot showing Designer and Transmit looking at the same WebContent in an NSF" title="Screenshot showing Designer and Transmit looking at the same WebContent in an NSF" border="0" width="516" height="337" /&gt;
  160. &lt;p&gt;The idea here is that this could be used to deploy, say, a JavaScript client application to an NSF without the developer or build server having to know anything about Domino. Pretty much everything can work with SFTP, so this makes accessing those files a lot easier. This is similar to the &lt;a href="https://help.hcltechsw.com/domino/14.0.0/admin/conf_settingupandenablingwebdav_t.html"&gt;WebDAV capabilities Domino has had for a very long time&lt;/a&gt;, but with a different protocol.&lt;/p&gt;
  161. &lt;h3&gt;Server Keypairs&lt;/h3&gt;
  162. &lt;p&gt;In the first version, the app would generate and store the server's SSH keypair on the filesystem, in the data directory. This is &lt;em&gt;fine&lt;/em&gt;, but part of the point of this whole project is that I like to get away from non-replicating stuff, and so I moved these keys to the configuration NSF. Now, on first connection, the server will look for a keypair document in the NSF and, if it doesn't exist, will generate a new one and store it there. Since I've been working with encrypted fields a lot for client work lately, I also realized that this was a good use for it: the public key is a normal text item (so you could distribute and verify it as needed), but the private key is encrypted with the generating server's ID file. Since only the server itself ever needs to know its  private key, this works swimmingly.&lt;/p&gt;
  163. &lt;h3&gt;JNX&lt;/h3&gt;
  164. &lt;p&gt;This isn't a new app feature per se, but this was a good situation for me to put &lt;a href="https://github.com/HCL-TECH-SOFTWARE/domino-jnx"&gt;JNX&lt;/a&gt; to work in an open-source project. I had originally written this using the &lt;code&gt;lotus.domino&lt;/code&gt; classes for most work and the IBM NAPI for things like generating sessions for a given username, but switching to JNX let me ditch both of those.&lt;/p&gt;
  165. &lt;p&gt;Admittedly, this is a case where switching to JNX didn't grant me significant new capabilities, but it DID let me do a couple things better. Some things are distinct feature improvements, like improving password authentication (previously, I was doing a compare of hashes &amp;quot;manually&amp;quot;, which is fragile), while others are just making the code smoother, like no longer having to do the read-convert-recycle dance with &lt;code&gt;DateTime&lt;/code&gt;s in LSXBE. It's just pleasant, and let me find a few places where the JNX API could be improved.&lt;/p&gt;
  166. &lt;h3&gt;Future Additions&lt;/h3&gt;
  167. &lt;p&gt;When I pick this project back up, there are certainly a couple things I'd like to add.&lt;/p&gt;
  168. &lt;p&gt;One would be to look into rsync support: rsync is tremendously useful for things like synchronizing filesystem-bound configs, but it's its own protocol tunneled over SSH, and so just having SFTP isn't enough. The underlying &lt;a href="https://mina.apache.org/sshd-project/"&gt;Apache Mina SSHD project&lt;/a&gt; is a general SSH server and not just SFTP, so it may be possible to do it by intercepting the commands sent over to initialize rsync, but it will be non-trivial. There's a &lt;a href="https://github.com/perlundq/yajsync"&gt;library in Java that provides an rsync server&lt;/a&gt;, but it's GPL-licensed, and so I have to keep away for license-safety's sake.&lt;/p&gt;
  169. &lt;p&gt;Beyond that, it's mostly that I'd like to implement more filesystem types. Presenting data as a filesystem can be a very powerful tool: you could imagine providing access to documents in a DB as DXL or YAML, or listing files from a Document Library NSF, or (as I'd like to do some day) having the NSF ODP Tooling project replicate the ODP layout over SFTP.&lt;/p&gt;
  170. &lt;p&gt;For now, I'm looking forward to putting it to more use as a coordinating point. If I keep messing around with &lt;a href="/blog/posts/2024/3/2/homelab-rework-phase-3-truenas-core-to-scale"&gt;apps on TrueNAS&lt;/a&gt;, it'll give me a good feeling of security to have more info stashed in Domino and less prone to destruction if one server happens to blow up.&lt;/p&gt;
  171. </content:encoded><guid>946BE02C88834836AF17D232DDEFB57F</guid><dc:creator>Jesse Gallagher</dc:creator><dc:date>2024-04-07T17:59:18.579Z</dc:date><pubDate>Sun, 7 Apr 2024 17:59:18 GMT</pubDate><description>&lt;p&gt;A few years ago, I made a little project that &lt;a href="/blog/posts/2019/11/5/another-side-project-nsf-sftp-file-store"&gt;hosts an SFTP server that stores documents in an NSF&lt;/a&gt;. I've used it here and there since then - as in the original post, I stashed some company docs in it to have them nicely synced among our Domino servers, and I've also had cases where clients use it to, for example, provide a way for their vendors to upload files in a standard way.&lt;/p&gt;
  172. &lt;p&gt;The other week, I decided to dive back into it to add some capabilities I'd wanted for a while, and the result is &lt;a href="https://www.openntf.org/main.nsf/project.xsp?r=project/640CDDAE57EAAC6A86258AF8005DEF10/releases/95FED8031FC3659286258AF8005E1963"&gt;version 2.0.0&lt;/a&gt;. This version is a significant revamp that adds quite a bit.&lt;/p&gt;
  173. &lt;h3&gt;Multiple Mounts&lt;/h3&gt;
  174. &lt;p&gt;The first limitation I wanted to improve was the fact that the first version was restricted to a single NSF. That works fine in the basic case, but I wanted to start doing things like storing server config backups in there, and wouldn't necessarily want them in the same NSF as, say, company contracts, credentials, and secrets.&lt;/p&gt;
  175. &lt;p&gt;The way I went about this was to make it so that the new configuration NSF has a &amp;quot;Mounts&amp;quot; view that lets you specify a path in a conceptual top level of the filesystem that would then point to a target NSF. This allows the admin to do things like have separate ACLs for different mounts - since the client will act as an authenticated Domino user, these will be properly obeyed, and a user won't be able to access documents they don't have rights to.&lt;/p&gt;
  176. &lt;p&gt;Additionally, I could configure it so that not all mounts are present on all servers, which will come into play particularly with the next feature.&lt;/p&gt;
  177. &lt;img src="media/C8BDF19A6CCD429FBA0EE561FEF890B3/sftp-mounts.png" alt="Screenshot of the Mounts view in the file server config NSF" title="Screenshot of the Mounts view in the file server config NSF" border="0" width="685" height="209" /&gt;
  178. &lt;h3&gt;New Filesystem Types&lt;/h3&gt;
  179. &lt;p&gt;Once I had a composite top-level filesystem, I realized that it wouldn't be terribly difficult to allow more filesystem types than the original NSF file store I made. That filesystem is built using the &lt;a href="https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/filesystemprovider.html"&gt;NIO File System Provider&lt;/a&gt; framework added in Java 7, and that system is designed to be pretty extensible. By default, Java comes with a few providers: the normal local filesystem as well as one that can treat a ZIP or JAR as a contained filesystem itself. These are accessed in a generic way, where you specify a URI and a map of &amp;quot;env&amp;quot; properties that are provider-specific.&lt;/p&gt;
  180. &lt;p&gt;For example, the &lt;a href="https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.html"&gt;ZIP filesystem&lt;/a&gt; takes a URI in the form of &amp;quot;jar:file:/some/path/to/file.zip&amp;quot; and an environment map configuring whether the filesystem should be created if it doesn't already exist in memory and what encoding to use for filenames (very important if you have Unicode characters in there).&lt;/p&gt;
  181. &lt;p&gt;So I added ways to configure a mount to the local server filesystem (similar to what the &lt;a href="https://www.openntf.org/internal/home.nsf/project.xsp?action=openDocument&amp;amp;name=Mindoo%20FTP%20Server"&gt;Mindoo FTP Server&lt;/a&gt; does) and then a generic configuration for any installed provider. It's probably uncommon that you will have a custom File System Provider implementation in your Java classpath, but hey, maybe you do, and I want to allow that to work.&lt;/p&gt;
  182. &lt;p&gt;I also added an extension point to the project itself that allows adding new providers via plugin.xml files in OSGi, and I can think of a couple other projects that may use this, like the NSF ODP Tooling.&lt;/p&gt;
  183. &lt;h3&gt;WebContent Filesystem&lt;/h3&gt;
  184. &lt;p&gt;Beyond adding the JVM-provided systems, I wrote another new filesystem type, one that provides access to the conceptual &amp;quot;WebContent&amp;quot; directory presented in Package Explorer in Designer:&lt;/p&gt;
  185. &lt;img src="media/ECC241D1172F47408B85B8DF2EAAF777/sftp-webcontent.png" alt="Screenshot showing Designer and Transmit looking at the same WebContent in an NSF" title="Screenshot showing Designer and Transmit looking at the same WebContent in an NSF" border="0" width="516" height="337" /&gt;
  186. &lt;p&gt;The idea here is that this could be used to deploy, say, a JavaScript client application to an NSF without the developer or build server having to know anything about Domino. Pretty much everything can work with SFTP, so this makes accessing those files a lot easier. This is similar to the &lt;a href="https://help.hcltechsw.com/domino/14.0.0/admin/conf_settingupandenablingwebdav_t.html"&gt;WebDAV capabilities Domino has had for a very long time&lt;/a&gt;, but with a different protocol.&lt;/p&gt;
  187. &lt;h3&gt;Server Keypairs&lt;/h3&gt;
  188. &lt;p&gt;In the first version, the app would generate and store the server's SSH keypair on the filesystem, in the data directory. This is &lt;em&gt;fine&lt;/em&gt;, but part of the point of this whole project is that I like to get away from non-replicating stuff, and so I moved these keys to the configuration NSF. Now, on first connection, the server will look for a keypair document in the NSF and, if it doesn't exist, will generate a new one and store it there. Since I've been working with encrypted fields a lot for client work lately, I also realized that this was a good use for it: the public key is a normal text item (so you could distribute and verify it as needed), but the private key is encrypted with the generating server's ID file. Since only the server itself ever needs to know its  private key, this works swimmingly.&lt;/p&gt;
  189. &lt;h3&gt;JNX&lt;/h3&gt;
  190. &lt;p&gt;This isn't a new app feature per se, but this was a good situation for me to put &lt;a href="https://github.com/HCL-TECH-SOFTWARE/domino-jnx"&gt;JNX&lt;/a&gt; to work in an open-source project. I had originally written this using the &lt;code&gt;lotus.domino&lt;/code&gt; classes for most work and the IBM NAPI for things like generating sessions for a given username, but switching to JNX let me ditch both of those.&lt;/p&gt;
  191. &lt;p&gt;Admittedly, this is a case where switching to JNX didn't grant me significant new capabilities, but it DID let me do a couple things better. Some things are distinct feature improvements, like improving password authentication (previously, I was doing a compare of hashes &amp;quot;manually&amp;quot;, which is fragile), while others are just making the code smoother, like no longer having to do the read-convert-recycle dance with &lt;code&gt;DateTime&lt;/code&gt;s in LSXBE. It's just pleasant, and let me find a few places where the JNX API could be improved.&lt;/p&gt;
  192. &lt;h3&gt;Future Additions&lt;/h3&gt;
  193. &lt;p&gt;When I pick this project back up, there are certainly a couple things I'd like to add.&lt;/p&gt;
  194. &lt;p&gt;One would be to look into rsync support: rsync is tremendously useful for things like synchronizing filesystem-bound configs, but it's its own protocol tunneled over SSH, and so just having SFTP isn't enough. The underlying &lt;a href="https://mina.apache.org/sshd-project/"&gt;Apache Mina SSHD project&lt;/a&gt; is a general SSH server and not just SFTP, so it may be possible to do it by intercepting the commands sent over to initialize rsync, but it will be non-trivial. There's a &lt;a href="https://github.com/perlundq/yajsync"&gt;library in Java that provides an rsync server&lt;/a&gt;, but it's GPL-licensed, and so I have to keep away for license-safety's sake.&lt;/p&gt;
  195. &lt;p&gt;Beyond that, it's mostly that I'd like to implement more filesystem types. Presenting data as a filesystem can be a very powerful tool: you could imagine providing access to documents in a DB as DXL or YAML, or listing files from a Document Library NSF, or (as I'd like to do some day) having the NSF ODP Tooling project replicate the ODP layout over SFTP.&lt;/p&gt;
  196. &lt;p&gt;For now, I'm looking forward to putting it to more use as a coordinating point. If I keep messing around with &lt;a href="/blog/posts/2024/3/2/homelab-rework-phase-3-truenas-core-to-scale"&gt;apps on TrueNAS&lt;/a&gt;, it'll give me a good feeling of security to have more info stashed in Domino and less prone to destruction if one server happens to blow up.&lt;/p&gt;
  197. </description></item><item><title>Realmz</title><link>https://frostillic.us/blog/posts/2024/3/31/realmz</link><content:encoded>&lt;p&gt;For a while now, I've wanted to just kind of gush about an old Mac game I played when I was a teenager, and the last day of &lt;a href="https://mastodon.social/tags/MARCHintosh"&gt;Marchintosh&lt;/a&gt; for the year is as good a time as any.&lt;/p&gt;
  198. &lt;h3&gt;Overview&lt;/h3&gt;
  199. &lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Realmz"&gt;Realmz&lt;/a&gt; is a game that ran on the &lt;a href="https://en.wikipedia.org/wiki/Classic_Mac_OS"&gt;classic Mac OS&lt;/a&gt; and, in later versions, Windows. It was shareware at the time - one of the few shareware games I ended up cobbling together the money for - but has long been made fully available for free, with my go-to source being &lt;a href="https://macintoshgarden.org/games/realmz"&gt;the Macintosh Garden&lt;/a&gt;. If you have &lt;a href="https://en.wikipedia.org/wiki/SheepShaver"&gt;SheepShaver&lt;/a&gt; around, it works nicely there.&lt;/p&gt;
  200. &lt;p&gt;The game itself is quickly identified as a party-based fantasy RPG. I didn't really realize it at the time, but it's a full-on CRPG in the nerdiest sense. I mean, look at this freaking character sheet:&lt;/p&gt;
  201. &lt;img src="media/BEAB4D5A5DE842EBA3E170FE9CB5B579/realmz-chargen.png" alt="Screenshot of the Realmz character creation sheet" title="Screenshot of the Realmz character creation sheet" border="0" width="1277" height="958" /&gt;
  202. &lt;p&gt;While it's not strictly D&amp;amp;D rules, it basically is. Older versions (which are also available on the Macintosh Garden) even used &lt;a href="https://en.wikipedia.org/wiki/Role-playing_game_terms#T"&gt;THAC0&lt;/a&gt; before switching to an &amp;quot;Armor Rating&amp;quot; system.&lt;/p&gt;
  203. &lt;h3&gt;CRPG&lt;/h3&gt;
  204. &lt;p&gt;Looking back, I'm glad I had an experience with such a true-blood CRPG at the time. I didn't play D&amp;amp;D growing up, didn't play the &lt;a href="https://en.wikipedia.org/wiki/Gold_Box"&gt;Gold Box games&lt;/a&gt;, and was too busy playing pretty much exclusively Blizzard games to play the Infinity Engine games or Neverwinter Nights when they came out. It wasn't really until Dragon Age: Origins and then (especially) Pillars of Eternity that I realized the glory of the genre. But looking at Realmz, it's obvious that it's right in the same lineage.&lt;/p&gt;
  205. &lt;p&gt;Combat is strictly turn-based, takes place on a grid, and is suitably technical:&lt;/p&gt;
  206. &lt;img src="media/A3B2D03BC0814001937561F16FB26020/realmz-combat.png" alt="Screenshot of a combat situation in Realmz" title="Screenshot of a combat situation in Realmz" border="0" width="1600" height="1148" /&gt;
  207. &lt;p&gt;It even does some of the weird stuff: for example, martial characters won't just get multiple attacks per round, but will also get &amp;quot;partial&amp;quot; steps like my rogue Hebs there, who gets three attacks every two rounds, as a stepping stone to 2 / 1.&lt;/p&gt;
  208. &lt;p&gt;Realmz also has its own mechanics-heavy take on the thing CRPGs try to do where they want to emulate an open-ended experience a DM might oversee beyond just combat. For example, early on, you meet a kid who wants you to help his dog, which is stuck in a well. When you get there, you're presented with the &amp;quot;encounter&amp;quot; screen, where you can try all sorts of things:&lt;/p&gt;
  209. &lt;img src="media/35BE8F96A79E476788910CFE0FA4AC94/realmz-encounter.png" alt="Screenshot of the Realmz encounter screen" title="realmz-encounter.png" border="0" width="1599" height="1149" /&gt;
  210. &lt;p&gt;There are a lot of ways to deal with these encounters. In this case, I might have Galba there do an Acrobatic Act, which has about even odds. My sorcerer Fenton there might use a Spider Climb (might not be the name) spell to make scaling the well effortless. Or, if I stocked up, I might just use a rope. You can easily fail this - if you do, the kid runs off crying and you have to wait for the guards to show up to help you, with no experience gain. Realmz has a bunch of these scenarios and they're pretty neat. Admittedly, they fall short in the ways that all non-DM-run games eventually do, where your actual options aren't truly limitless. The &amp;quot;Speak&amp;quot; option is available in other situations, but it's only ever really practical if you have, say, a magic word to open a door or something. It's not a true tabletop experience, but it's trying, bless its heart.&lt;/p&gt;
  211. &lt;h3&gt;Mac-ness&lt;/h3&gt;
  212. &lt;p&gt;One thing I really enjoy about games in the heyday of Mac shareware games (by the way, read &lt;a href="https://secrethistoryofmacgaming.com/"&gt;The Secret History of Mac Gaming&lt;/a&gt; if you haven't - it's great) is how thoroughly &lt;em&gt;Mac-like&lt;/em&gt; they are. For both practical and cultural reasons, a lot of Mac games didn't necessarily take over the whole screen with their own interface like DOS and Windows games usually do. While there are some Windows games that use the Windows UI, like another small classic &lt;a href="https://en.wikipedia.org/wiki/Castle_of_the_Winds"&gt;Castle of the Winds&lt;/a&gt;, it's very common in Mac games. For example, there's &lt;a href="https://macintoshgarden.org/games/scarab-of-ra"&gt;Scarab of Ra&lt;/a&gt;:&lt;/p&gt;
  213. &lt;img src="media/6F84C2747CD340D2904C20DBCBCEC769/scarab.jpg" alt="Screenshot of Scarab of Ra from Macintosh Garden" title="Screenshot of Scarab of Ra from Macintosh Garden" border="0" width="509" height="340" /&gt;
  214. &lt;p&gt;As it happens, Scarab of Ra is another game where I didn't appreciate its lineage at the time: it's a true &lt;a href="https://en.wikipedia.org/wiki/Roguelike"&gt;roguelike&lt;/a&gt;, albeit with a first-person perspective.&lt;/p&gt;
  215. &lt;p&gt;Realmz doesn't go &lt;em&gt;quite&lt;/em&gt; as hard in using native widgets for everything, but you can see the menu bar in earlier screenshots - you use the normal Mac menu to access game commands, the bestiary, your ally list, your collected notes, and so forth. It's just neat. Also, like a lot of Mac software at the time, Realmz's program directory is just a delight to look at:&lt;/p&gt;
  216. &lt;img src="media/80F79DFC94C74628B35781D4BB746E8B/realmz-folder.png" alt="Screenshot of the Realmz 2.5 installation folder" title="Screenshot of the Realmz 2.5 installation folder" border="0" width="887" height="674" /&gt;
  217. &lt;p&gt;My use of the &amp;quot;Drawing Board&amp;quot; Appearance Manager theme helps it too, but just check out those icons. That sort of thing wasn't strictly necessary, but it was the Mac way, and it was wonderful.&lt;/p&gt;
  218. &lt;h3&gt;Versions&lt;/h3&gt;
  219. &lt;p&gt;And this isn't exactly a Mac-like attribute, but I like that Realmz wasn't afraid of using version numbers. It went from version 1.x all the way up through 8.x, with minor and patch versions along the way. It was updated all the time, and it was always exciting to see a new major version to find what the big changes are.&lt;/p&gt;
  220. &lt;p&gt;Mostly, the changes were things like adding classes: the old versions have the same sort of handful you'd find in basic D&amp;amp;D, while the later ones have so many that you can pick between &amp;quot;Archer&amp;quot; and &amp;quot;Marksman&amp;quot; or &amp;quot;Bard&amp;quot; and &amp;quot;Minstrel&amp;quot;. Some of the changes were less like finding a D&amp;amp;D source book and more like the game gradually morphing into its own sequel, though.&lt;/p&gt;
  221. &lt;p&gt;For example, the original versions didn't have music of any kind, as was the style at the time. Somewhere along the line (version 5, I think), it gained music, and... boy, &lt;a href="https://www.youtube.com/playlist?list=PLAA7997C286D408EC"&gt;it's a doozy&lt;/a&gt;. Here's, for example, the camping music:&lt;/p&gt;
  222. &lt;iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/qNTaoOtmCcM?si=Gt7Nv6oytUbD3tgP" title="YouTube video player" frameborder="0" allow="clipboard-write; encrypted-media; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;
  223. &lt;p&gt;What I assume happened is that the developer wanted to add some music and then found some free or cheap &lt;a href="https://en.wikipedia.org/wiki/Module_file"&gt;module files&lt;/a&gt; and slapped them in there where they kind of work. The tone is absolutely bizarre, and it's kind of great for it.&lt;/p&gt;
  224. &lt;p&gt;It was just neat seeing the game progress, with the changes in systems and new features, even the &amp;quot;eh, not the best idea&amp;quot; stuff like parts of dungeons that switch to a first-person mode.&lt;/p&gt;
  225. &lt;h3&gt;Scenarios&lt;/h3&gt;
  226. &lt;p&gt;I have to admit that, though I played a ton of Realmz, I never even got that far into it. A big part of that was that the scenarios beyond the starting City of Bywater also cost money above the core game, and it's a tall order for a cash-strapped teenager to cough up money at all, let alone add-on costs. So my characters probably always plateaued around level 10, as there's just not that much to do in the base scenario. That's good news for future me, though: there's a ton of stuff waiting for me whenever I want to get back into it.&lt;/p&gt;
  227. &lt;p&gt;If you have a taste for older games or CRPGs in general, I definitely suggest you give it a try. The Windows version may be easier to run than the Mac one, though it loses some of the appeal. Depending on your temperament, Realmz may be easier to get into from scratch than games like, say, the original Baldur's Gate, with the latter's janky RTwP combat and constant fourth-wall-breaking dweebiness. Definitely keep it in mind for a cozy-day game, I say.&lt;/p&gt;
  228. </content:encoded><guid>DCFDBE0B4E8248B6A3E16EF299E6D44E</guid><dc:creator>Jesse Gallagher</dc:creator><dc:date>2024-03-31T15:35:14.388Z</dc:date><pubDate>Sun, 31 Mar 2024 15:35:14 GMT</pubDate><description>&lt;p&gt;For a while now, I've wanted to just kind of gush about an old Mac game I played when I was a teenager, and the last day of &lt;a href="https://mastodon.social/tags/MARCHintosh"&gt;Marchintosh&lt;/a&gt; for the year is as good a time as any.&lt;/p&gt;
  229. &lt;h3&gt;Overview&lt;/h3&gt;
  230. &lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Realmz"&gt;Realmz&lt;/a&gt; is a game that ran on the &lt;a href="https://en.wikipedia.org/wiki/Classic_Mac_OS"&gt;classic Mac OS&lt;/a&gt; and, in later versions, Windows. It was shareware at the time - one of the few shareware games I ended up cobbling together the money for - but has long been made fully available for free, with my go-to source being &lt;a href="https://macintoshgarden.org/games/realmz"&gt;the Macintosh Garden&lt;/a&gt;. If you have &lt;a href="https://en.wikipedia.org/wiki/SheepShaver"&gt;SheepShaver&lt;/a&gt; around, it works nicely there.&lt;/p&gt;
  231. &lt;p&gt;The game itself is quickly identified as a party-based fantasy RPG. I didn't really realize it at the time, but it's a full-on CRPG in the nerdiest sense. I mean, look at this freaking character sheet:&lt;/p&gt;
  232. &lt;img src="media/BEAB4D5A5DE842EBA3E170FE9CB5B579/realmz-chargen.png" alt="Screenshot of the Realmz character creation sheet" title="Screenshot of the Realmz character creation sheet" border="0" width="1277" height="958" /&gt;
  233. &lt;p&gt;While it's not strictly D&amp;amp;D rules, it basically is. Older versions (which are also available on the Macintosh Garden) even used &lt;a href="https://en.wikipedia.org/wiki/Role-playing_game_terms#T"&gt;THAC0&lt;/a&gt; before switching to an &amp;quot;Armor Rating&amp;quot; system.&lt;/p&gt;
  234. &lt;h3&gt;CRPG&lt;/h3&gt;
  235. &lt;p&gt;Looking back, I'm glad I had an experience with such a true-blood CRPG at the time. I didn't play D&amp;amp;D growing up, didn't play the &lt;a href="https://en.wikipedia.org/wiki/Gold_Box"&gt;Gold Box games&lt;/a&gt;, and was too busy playing pretty much exclusively Blizzard games to play the Infinity Engine games or Neverwinter Nights when they came out. It wasn't really until Dragon Age: Origins and then (especially) Pillars of Eternity that I realized the glory of the genre. But looking at Realmz, it's obvious that it's right in the same lineage.&lt;/p&gt;
  236. &lt;p&gt;Combat is strictly turn-based, takes place on a grid, and is suitably technical:&lt;/p&gt;
  237. &lt;img src="media/A3B2D03BC0814001937561F16FB26020/realmz-combat.png" alt="Screenshot of a combat situation in Realmz" title="Screenshot of a combat situation in Realmz" border="0" width="1600" height="1148" /&gt;
  238. &lt;p&gt;It even does some of the weird stuff: for example, martial characters won't just get multiple attacks per round, but will also get &amp;quot;partial&amp;quot; steps like my rogue Hebs there, who gets three attacks every two rounds, as a stepping stone to 2 / 1.&lt;/p&gt;
  239. &lt;p&gt;Realmz also has its own mechanics-heavy take on the thing CRPGs try to do where they want to emulate an open-ended experience a DM might oversee beyond just combat. For example, early on, you meet a kid who wants you to help his dog, which is stuck in a well. When you get there, you're presented with the &amp;quot;encounter&amp;quot; screen, where you can try all sorts of things:&lt;/p&gt;
  240. &lt;img src="media/35BE8F96A79E476788910CFE0FA4AC94/realmz-encounter.png" alt="Screenshot of the Realmz encounter screen" title="realmz-encounter.png" border="0" width="1599" height="1149" /&gt;
  241. &lt;p&gt;There are a lot of ways to deal with these encounters. In this case, I might have Galba there do an Acrobatic Act, which has about even odds. My sorcerer Fenton there might use a Spider Climb (might not be the name) spell to make scaling the well effortless. Or, if I stocked up, I might just use a rope. You can easily fail this - if you do, the kid runs off crying and you have to wait for the guards to show up to help you, with no experience gain. Realmz has a bunch of these scenarios and they're pretty neat. Admittedly, they fall short in the ways that all non-DM-run games eventually do, where your actual options aren't truly limitless. The &amp;quot;Speak&amp;quot; option is available in other situations, but it's only ever really practical if you have, say, a magic word to open a door or something. It's not a true tabletop experience, but it's trying, bless its heart.&lt;/p&gt;
  242. &lt;h3&gt;Mac-ness&lt;/h3&gt;
  243. &lt;p&gt;One thing I really enjoy about games in the heyday of Mac shareware games (by the way, read &lt;a href="https://secrethistoryofmacgaming.com/"&gt;The Secret History of Mac Gaming&lt;/a&gt; if you haven't - it's great) is how thoroughly &lt;em&gt;Mac-like&lt;/em&gt; they are. For both practical and cultural reasons, a lot of Mac games didn't necessarily take over the whole screen with their own interface like DOS and Windows games usually do. While there are some Windows games that use the Windows UI, like another small classic &lt;a href="https://en.wikipedia.org/wiki/Castle_of_the_Winds"&gt;Castle of the Winds&lt;/a&gt;, it's very common in Mac games. For example, there's &lt;a href="https://macintoshgarden.org/games/scarab-of-ra"&gt;Scarab of Ra&lt;/a&gt;:&lt;/p&gt;
  244. &lt;img src="media/6F84C2747CD340D2904C20DBCBCEC769/scarab.jpg" alt="Screenshot of Scarab of Ra from Macintosh Garden" title="Screenshot of Scarab of Ra from Macintosh Garden" border="0" width="509" height="340" /&gt;
  245. &lt;p&gt;As it happens, Scarab of Ra is another game where I didn't appreciate its lineage at the time: it's a true &lt;a href="https://en.wikipedia.org/wiki/Roguelike"&gt;roguelike&lt;/a&gt;, albeit with a first-person perspective.&lt;/p&gt;
  246. &lt;p&gt;Realmz doesn't go &lt;em&gt;quite&lt;/em&gt; as hard in using native widgets for everything, but you can see the menu bar in earlier screenshots - you use the normal Mac menu to access game commands, the bestiary, your ally list, your collected notes, and so forth. It's just neat. Also, like a lot of Mac software at the time, Realmz's program directory is just a delight to look at:&lt;/p&gt;
  247. &lt;img src="media/80F79DFC94C74628B35781D4BB746E8B/realmz-folder.png" alt="Screenshot of the Realmz 2.5 installation folder" title="Screenshot of the Realmz 2.5 installation folder" border="0" width="887" height="674" /&gt;
  248. &lt;p&gt;My use of the &amp;quot;Drawing Board&amp;quot; Appearance Manager theme helps it too, but just check out those icons. That sort of thing wasn't strictly necessary, but it was the Mac way, and it was wonderful.&lt;/p&gt;
  249. &lt;h3&gt;Versions&lt;/h3&gt;
  250. &lt;p&gt;And this isn't exactly a Mac-like attribute, but I like that Realmz wasn't afraid of using version numbers. It went from version 1.x all the way up through 8.x, with minor and patch versions along the way. It was updated all the time, and it was always exciting to see a new major version to find what the big changes are.&lt;/p&gt;
  251. &lt;p&gt;Mostly, the changes were things like adding classes: the old versions have the same sort of handful you'd find in basic D&amp;amp;D, while the later ones have so many that you can pick between &amp;quot;Archer&amp;quot; and &amp;quot;Marksman&amp;quot; or &amp;quot;Bard&amp;quot; and &amp;quot;Minstrel&amp;quot;. Some of the changes were less like finding a D&amp;amp;D source book and more like the game gradually morphing into its own sequel, though.&lt;/p&gt;
  252. &lt;p&gt;For example, the original versions didn't have music of any kind, as was the style at the time. Somewhere along the line (version 5, I think), it gained music, and... boy, &lt;a href="https://www.youtube.com/playlist?list=PLAA7997C286D408EC"&gt;it's a doozy&lt;/a&gt;. Here's, for example, the camping music:&lt;/p&gt;
  253. &lt;iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/qNTaoOtmCcM?si=Gt7Nv6oytUbD3tgP" title="YouTube video player" frameborder="0" allow="clipboard-write; encrypted-media; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;
  254. &lt;p&gt;What I assume happened is that the developer wanted to add some music and then found some free or cheap &lt;a href="https://en.wikipedia.org/wiki/Module_file"&gt;module files&lt;/a&gt; and slapped them in there where they kind of work. The tone is absolutely bizarre, and it's kind of great for it.&lt;/p&gt;
  255. &lt;p&gt;It was just neat seeing the game progress, with the changes in systems and new features, even the &amp;quot;eh, not the best idea&amp;quot; stuff like parts of dungeons that switch to a first-person mode.&lt;/p&gt;
  256. &lt;h3&gt;Scenarios&lt;/h3&gt;
  257. &lt;p&gt;I have to admit that, though I played a ton of Realmz, I never even got that far into it. A big part of that was that the scenarios beyond the starting City of Bywater also cost money above the core game, and it's a tall order for a cash-strapped teenager to cough up money at all, let alone add-on costs. So my characters probably always plateaued around level 10, as there's just not that much to do in the base scenario. That's good news for future me, though: there's a ton of stuff waiting for me whenever I want to get back into it.&lt;/p&gt;
  258. &lt;p&gt;If you have a taste for older games or CRPGs in general, I definitely suggest you give it a try. The Windows version may be easier to run than the Mac one, though it loses some of the appeal. Depending on your temperament, Realmz may be easier to get into from scratch than games like, say, the original Baldur's Gate, with the latter's janky RTwP combat and constant fourth-wall-breaking dweebiness. Definitely keep it in mind for a cozy-day game, I say.&lt;/p&gt;
  259. </description></item><item><title>Adding Code Coverage Reports To Domino-Container-Run Tests</title><link>https://frostillic.us/blog/posts/2024/3/11/adding-code-coverage-reports-to-domino-container-run-tests</link><content:encoded>&lt;p&gt;When you're writing test suites for your code, it can be very useful to use a tool to analyze the &lt;a href="https://en.wikipedia.org/wiki/Code_coverage"&gt;code coverage&lt;/a&gt; of your tests. While people can get a little obsessive about coverage percents, there's certainly no denying that it's helpful to know how much of your code is actually run when testing, and also being able to look down into the specifics of &lt;em&gt;what&lt;/em&gt; is covered.&lt;/p&gt;
  260. &lt;p&gt;With Java, one of the preeminent tools for this is &lt;a href="https://www.jacoco.org/jacoco/"&gt;JaCoCo&lt;/a&gt;, a venerable open-source library that you can integrate with your test suites to give reports of your coverage. In a normal project, such as a build run via Maven, you can use the &lt;a href="https://www.jacoco.org/jacoco/trunk/doc/maven.html"&gt;Maven plugin&lt;/a&gt; in tandem with the Maven &lt;a href="https://maven.apache.org/surefire/maven-surefire-plugin/"&gt;Surefire&lt;/a&gt; and &lt;a href="https://maven.apache.org/surefire/maven-failsafe-plugin/"&gt;Failsafe&lt;/a&gt; plugins. However, things get more complicated if the code you're actually testing isn't in the Surefire JVM, but rather inside a container.&lt;/p&gt;
  261. &lt;p&gt;That's exactly the situation I have with the integration-test suite of the &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee"&gt;XPages Jakarta EE project&lt;/a&gt;, where it creates a Docker container with the current build of the project deployed as OSGi plugins, and then executes HTTP calls against OSGi bundles and NSFs. I figured this was a solvable problem, so I set out doing so.&lt;/p&gt;
  262. &lt;p&gt;I first came across &lt;a href="https://medium.com/@jdmwood/integration-test-code-coverage-with-java-docker-jacoco-132f63e2f083"&gt;this blog post&lt;/a&gt;, which describes the general idea well, but unfortunately references Gists that seem to no longer exist. Still, it gave me a good starting point.&lt;/p&gt;
  263. &lt;h3&gt;Installing JaCoCo in Domino&lt;/h3&gt;
  264. &lt;p&gt;The first thing I had to do was to get the JaCoCo &lt;a href="https://www.baeldung.com/java-instrumentation"&gt;Java agent&lt;/a&gt; into the container. I added it as a Maven dependency to the IT suite project:&lt;/p&gt;
  265. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1
  266. 2
  267. 3
  268. 4
  269. 5
  270. 6&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;&lt;span style="color: #007700"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  271. &lt;span style="color: #007700"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jacoco&lt;span style="color: #007700"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  272. &lt;span style="color: #007700"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;org.jacoco.agent&lt;span style="color: #007700"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  273. &lt;span style="color: #007700"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.8.11&lt;span style="color: #007700"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  274. &lt;span style="color: #007700"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span style="color: #007700"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
  275. &lt;span style="color: #007700"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
  276. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  277. &lt;/div&gt;
  278. &lt;p&gt;Conveniently, this dependency is itself a wrapper for the agent JAR and comes with a convenience method for accessing the JAR data. I used that to read it into memory and send it to the Docker runtime during the container build:&lt;/p&gt;
  279. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1
  280. 2
  281. 3
  282. 4
  283. 5&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;&lt;span style="color: #333399; font-weight: bold"&gt;byte&lt;/span&gt;&lt;span style="color: #333333"&gt;[]&lt;/span&gt; agentData&lt;span style="color: #333333"&gt;;&lt;/span&gt;
  284. &lt;span style="color: #008800; font-weight: bold"&gt;try&lt;/span&gt;&lt;span style="color: #333333"&gt;(&lt;/span&gt;InputStream is &lt;span style="color: #333333"&gt;=&lt;/span&gt; AgentJar&lt;span style="color: #333333"&gt;.&lt;/span&gt;&lt;span style="color: #0000CC"&gt;getResourceAsStream&lt;/span&gt;&lt;span style="color: #333333"&gt;())&lt;/span&gt; &lt;span style="color: #333333"&gt;{&lt;/span&gt;
  285. agentData &lt;span style="color: #333333"&gt;=&lt;/span&gt; IOUtils&lt;span style="color: #333333"&gt;.&lt;/span&gt;&lt;span style="color: #0000CC"&gt;toByteArray&lt;/span&gt;&lt;span style="color: #333333"&gt;(&lt;/span&gt;is&lt;span style="color: #333333"&gt;);&lt;/span&gt;
  286. &lt;span style="color: #333333"&gt;}&lt;/span&gt;
  287. withFileFromTransferable&lt;span style="color: #333333"&gt;(&lt;/span&gt;&lt;span style="background-color: #fff0f0"&gt;&amp;quot;staging/jacoco.jar&amp;quot;&lt;/span&gt;&lt;span style="color: #333333"&gt;,&lt;/span&gt; Transferable&lt;span style="color: #333333"&gt;.&lt;/span&gt;&lt;span style="color: #0000CC"&gt;of&lt;/span&gt;&lt;span style="color: #333333"&gt;(&lt;/span&gt;agentData&lt;span style="color: #333333"&gt;));&lt;/span&gt; &lt;span style="color: #888888"&gt;//$NON-NLS-1$&lt;/span&gt;
  288. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  289. &lt;/div&gt;
  290. &lt;p&gt;The use of &lt;code&gt;Transferable&lt;/code&gt; here allows me to keep the process independent of whether Docker is running locally or remote - I run remotely almost all the time nowadays, due to Domino's continued lack of an ARM port.&lt;/p&gt;
  291. &lt;p&gt;With the file in place, I modified my Dockerfile to copy it to a known location in the container:&lt;/p&gt;
  292. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1
  293. 2&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;COPY --chown=notes:notes staging/jacoco.jar /local/
  294. COPY --chown=notes:notes staging/JavaOptionsFile.txt /local/
  295. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  296. &lt;/div&gt;
  297. &lt;p&gt;The JavaOptionsFile.txt was already there for another ARM-related reason, but it's important to note for the next step. This sort of file is how you enable JaCoCo in the Domino JVM: I set &lt;code&gt;JavaUserOptionsFile=/local/JavaOptionsFile.txt&lt;/code&gt; and it'll read its rules from there. Following the &lt;a href="https://www.jacoco.org/jacoco/trunk/doc/agent.html"&gt;instructions&lt;/a&gt;, I added &lt;code&gt;-javaagent:/local/jacoco.jar=output=file,destfile=/tmp/jacoco.exec&lt;/code&gt; on its own line in this file. This causes JaCoCo to be automatically loaded with the HTTP JVM and to store its report in the named file on shutdown.&lt;/p&gt;
  298. &lt;h3&gt;Reading the Data&lt;/h3&gt;
  299. &lt;p&gt;That said, this didn't work immediately. The file &amp;quot;/tmp/jacoco.exec&amp;quot; was created properly inside the container, so the agent was running, but the file content was always zero bytes. I realized that this was due to the merciless way in which the container is killed by my test suite: there's no proper shutdown step, and so JaCoCo's shutdown hook never fires.&lt;/p&gt;
  300. &lt;p&gt;Fortunately, writing to a file isn't the only way JaCoCo can do its reporting - you can also have it open up a TCP port to connect to and read. So I changed the Java option line to:&lt;/p&gt;
  301. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;-javaagent:/local/jacoco.jar=output=tcpserver,address=*,port=6300
  302. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  303. &lt;/div&gt;
  304. &lt;p&gt;I &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/blob/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/tests/it-xsp-jakartaee/src/test/java/it/org/openntf/xsp/jakartaee/docker/DominoContainer.java#L183"&gt;modified the &lt;code&gt;withExposedPorts(...)&lt;/code&gt; call&lt;/a&gt; inside the class that builds my Testcontainers container to also include 6300, and then used &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/blob/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/tests/it-xsp-jakartaee/src/test/java/it/org/openntf/xsp/jakartaee/docker/DominoContainer.java#L252"&gt;&lt;code&gt;getMappedPort(6300)&lt;/code&gt;&lt;/a&gt; to identify the actual randomized port mapped by Docker.&lt;/p&gt;
  305. &lt;p&gt;The remaining task was to figure out the little protocol used by JaCoCo to signal that it should collect and return its data. I get the impression that it's not too complicated, but I still figured it'd be best to use an existing implementation. I found &lt;a href="https://github.com/mattcj/jacocotogo"&gt;jacocotogo&lt;/a&gt;, a Maven plugin that reads the data, and it looked promising. However, it had two problems: being a Maven plugin, it came with a bunch of transitive dependencies I didn't want, and it's also 11 years old and thus a bit out of date.&lt;/p&gt;
  306. &lt;p&gt;I ended up &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/blob/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/tests/it-xsp-jakartaee/src/test/java/org/helmetsrequired/jacocotogo/JaCoCoToGo.java"&gt;forking the main utility class&lt;/a&gt;, trimming out the parts I didn't need (like JMX), switching it to NIO, and &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/blob/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/tests/it-xsp-jakartaee/src/test/java/it/org/openntf/xsp/jakartaee/docker/DominoContainer.java#L253"&gt;going from there&lt;/a&gt;.&lt;/p&gt;
  307. &lt;h3&gt;Using the Data&lt;/h3&gt;
  308. &lt;p&gt;With that all in place, a test run will end up with a file named &amp;quot;jacoco.exec&amp;quot; inside the &amp;quot;target&amp;quot; directory. Using this file varies by IDE, but, in Eclipse, you can install the &lt;a href="https://www.eclemma.org/"&gt;EclEmma&lt;/a&gt; tool, open the &amp;quot;Coverage&amp;quot; view, right-click in the table area, and choose &amp;quot;Import Session...&amp;quot;. That will let you locate the file and then choose the projects from your workspace that you're looking to analyze.&lt;/p&gt;
  309. &lt;p&gt;When I did that, I got my results:&lt;/p&gt;
  310. &lt;img src="media/DC11764D3186437889923B427FA275F1/coverage-view-results.png" alt="Screenshot of Eclipse&amp;#39;s Coverage tool detailing my test suite&amp;#39;s coverage of somewhere around 50-65%" title="Screenshot of Eclipse&amp;#39;s Coverage tool detailing my test suite&amp;#39;s coverage of somewhere around 50-65%" border="0" width="845" height="548" /&gt;
  311. &lt;p&gt;This is surprisingly good for the project, especially when you consider how large chunks of the red bars are things like the &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/tree/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/bundles/org.openntf.xsp.jakartaee.commons/src/org/openntf/xsp/jakartaee/servlet"&gt;servlet wrapper package&lt;/a&gt;, which includes a lot of delegating code that is obligatory to match the interface but is not likely to be actually used in practice.&lt;/p&gt;
  312. &lt;p&gt;While this is currently the only project where I've needed to do this, it'll certainly be good to keep these techniques in mind. The TCP port thing in particular should be handy in future edge cases even without the Docker part.&lt;/p&gt;
  313. </content:encoded><guid>F95A4709B344491F9AE5B1CB530CC300</guid><dc:creator>Jesse Gallagher</dc:creator><dc:date>2024-03-11T19:33:02.391Z</dc:date><pubDate>Mon, 11 Mar 2024 19:33:02 GMT</pubDate><description>&lt;p&gt;When you're writing test suites for your code, it can be very useful to use a tool to analyze the &lt;a href="https://en.wikipedia.org/wiki/Code_coverage"&gt;code coverage&lt;/a&gt; of your tests. While people can get a little obsessive about coverage percents, there's certainly no denying that it's helpful to know how much of your code is actually run when testing, and also being able to look down into the specifics of &lt;em&gt;what&lt;/em&gt; is covered.&lt;/p&gt;
  314. &lt;p&gt;With Java, one of the preeminent tools for this is &lt;a href="https://www.jacoco.org/jacoco/"&gt;JaCoCo&lt;/a&gt;, a venerable open-source library that you can integrate with your test suites to give reports of your coverage. In a normal project, such as a build run via Maven, you can use the &lt;a href="https://www.jacoco.org/jacoco/trunk/doc/maven.html"&gt;Maven plugin&lt;/a&gt; in tandem with the Maven &lt;a href="https://maven.apache.org/surefire/maven-surefire-plugin/"&gt;Surefire&lt;/a&gt; and &lt;a href="https://maven.apache.org/surefire/maven-failsafe-plugin/"&gt;Failsafe&lt;/a&gt; plugins. However, things get more complicated if the code you're actually testing isn't in the Surefire JVM, but rather inside a container.&lt;/p&gt;
  315. &lt;p&gt;That's exactly the situation I have with the integration-test suite of the &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee"&gt;XPages Jakarta EE project&lt;/a&gt;, where it creates a Docker container with the current build of the project deployed as OSGi plugins, and then executes HTTP calls against OSGi bundles and NSFs. I figured this was a solvable problem, so I set out doing so.&lt;/p&gt;
  316. &lt;p&gt;I first came across &lt;a href="https://medium.com/@jdmwood/integration-test-code-coverage-with-java-docker-jacoco-132f63e2f083"&gt;this blog post&lt;/a&gt;, which describes the general idea well, but unfortunately references Gists that seem to no longer exist. Still, it gave me a good starting point.&lt;/p&gt;
  317. &lt;h3&gt;Installing JaCoCo in Domino&lt;/h3&gt;
  318. &lt;p&gt;The first thing I had to do was to get the JaCoCo &lt;a href="https://www.baeldung.com/java-instrumentation"&gt;Java agent&lt;/a&gt; into the container. I added it as a Maven dependency to the IT suite project:&lt;/p&gt;
  319. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1
  320. 2
  321. 3
  322. 4
  323. 5
  324. 6&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;&lt;span style="color: #007700"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  325. &lt;span style="color: #007700"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jacoco&lt;span style="color: #007700"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  326. &lt;span style="color: #007700"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;org.jacoco.agent&lt;span style="color: #007700"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  327. &lt;span style="color: #007700"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.8.11&lt;span style="color: #007700"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  328. &lt;span style="color: #007700"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span style="color: #007700"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
  329. &lt;span style="color: #007700"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
  330. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  331. &lt;/div&gt;
  332. &lt;p&gt;Conveniently, this dependency is itself a wrapper for the agent JAR and comes with a convenience method for accessing the JAR data. I used that to read it into memory and send it to the Docker runtime during the container build:&lt;/p&gt;
  333. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1
  334. 2
  335. 3
  336. 4
  337. 5&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;&lt;span style="color: #333399; font-weight: bold"&gt;byte&lt;/span&gt;&lt;span style="color: #333333"&gt;[]&lt;/span&gt; agentData&lt;span style="color: #333333"&gt;;&lt;/span&gt;
  338. &lt;span style="color: #008800; font-weight: bold"&gt;try&lt;/span&gt;&lt;span style="color: #333333"&gt;(&lt;/span&gt;InputStream is &lt;span style="color: #333333"&gt;=&lt;/span&gt; AgentJar&lt;span style="color: #333333"&gt;.&lt;/span&gt;&lt;span style="color: #0000CC"&gt;getResourceAsStream&lt;/span&gt;&lt;span style="color: #333333"&gt;())&lt;/span&gt; &lt;span style="color: #333333"&gt;{&lt;/span&gt;
  339. agentData &lt;span style="color: #333333"&gt;=&lt;/span&gt; IOUtils&lt;span style="color: #333333"&gt;.&lt;/span&gt;&lt;span style="color: #0000CC"&gt;toByteArray&lt;/span&gt;&lt;span style="color: #333333"&gt;(&lt;/span&gt;is&lt;span style="color: #333333"&gt;);&lt;/span&gt;
  340. &lt;span style="color: #333333"&gt;}&lt;/span&gt;
  341. withFileFromTransferable&lt;span style="color: #333333"&gt;(&lt;/span&gt;&lt;span style="background-color: #fff0f0"&gt;&amp;quot;staging/jacoco.jar&amp;quot;&lt;/span&gt;&lt;span style="color: #333333"&gt;,&lt;/span&gt; Transferable&lt;span style="color: #333333"&gt;.&lt;/span&gt;&lt;span style="color: #0000CC"&gt;of&lt;/span&gt;&lt;span style="color: #333333"&gt;(&lt;/span&gt;agentData&lt;span style="color: #333333"&gt;));&lt;/span&gt; &lt;span style="color: #888888"&gt;//$NON-NLS-1$&lt;/span&gt;
  342. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  343. &lt;/div&gt;
  344. &lt;p&gt;The use of &lt;code&gt;Transferable&lt;/code&gt; here allows me to keep the process independent of whether Docker is running locally or remote - I run remotely almost all the time nowadays, due to Domino's continued lack of an ARM port.&lt;/p&gt;
  345. &lt;p&gt;With the file in place, I modified my Dockerfile to copy it to a known location in the container:&lt;/p&gt;
  346. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1
  347. 2&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;COPY --chown=notes:notes staging/jacoco.jar /local/
  348. COPY --chown=notes:notes staging/JavaOptionsFile.txt /local/
  349. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  350. &lt;/div&gt;
  351. &lt;p&gt;The JavaOptionsFile.txt was already there for another ARM-related reason, but it's important to note for the next step. This sort of file is how you enable JaCoCo in the Domino JVM: I set &lt;code&gt;JavaUserOptionsFile=/local/JavaOptionsFile.txt&lt;/code&gt; and it'll read its rules from there. Following the &lt;a href="https://www.jacoco.org/jacoco/trunk/doc/agent.html"&gt;instructions&lt;/a&gt;, I added &lt;code&gt;-javaagent:/local/jacoco.jar=output=file,destfile=/tmp/jacoco.exec&lt;/code&gt; on its own line in this file. This causes JaCoCo to be automatically loaded with the HTTP JVM and to store its report in the named file on shutdown.&lt;/p&gt;
  352. &lt;h3&gt;Reading the Data&lt;/h3&gt;
  353. &lt;p&gt;That said, this didn't work immediately. The file &amp;quot;/tmp/jacoco.exec&amp;quot; was created properly inside the container, so the agent was running, but the file content was always zero bytes. I realized that this was due to the merciless way in which the container is killed by my test suite: there's no proper shutdown step, and so JaCoCo's shutdown hook never fires.&lt;/p&gt;
  354. &lt;p&gt;Fortunately, writing to a file isn't the only way JaCoCo can do its reporting - you can also have it open up a TCP port to connect to and read. So I changed the Java option line to:&lt;/p&gt;
  355. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;-javaagent:/local/jacoco.jar=output=tcpserver,address=*,port=6300
  356. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  357. &lt;/div&gt;
  358. &lt;p&gt;I &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/blob/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/tests/it-xsp-jakartaee/src/test/java/it/org/openntf/xsp/jakartaee/docker/DominoContainer.java#L183"&gt;modified the &lt;code&gt;withExposedPorts(...)&lt;/code&gt; call&lt;/a&gt; inside the class that builds my Testcontainers container to also include 6300, and then used &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/blob/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/tests/it-xsp-jakartaee/src/test/java/it/org/openntf/xsp/jakartaee/docker/DominoContainer.java#L252"&gt;&lt;code&gt;getMappedPort(6300)&lt;/code&gt;&lt;/a&gt; to identify the actual randomized port mapped by Docker.&lt;/p&gt;
  359. &lt;p&gt;The remaining task was to figure out the little protocol used by JaCoCo to signal that it should collect and return its data. I get the impression that it's not too complicated, but I still figured it'd be best to use an existing implementation. I found &lt;a href="https://github.com/mattcj/jacocotogo"&gt;jacocotogo&lt;/a&gt;, a Maven plugin that reads the data, and it looked promising. However, it had two problems: being a Maven plugin, it came with a bunch of transitive dependencies I didn't want, and it's also 11 years old and thus a bit out of date.&lt;/p&gt;
  360. &lt;p&gt;I ended up &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/blob/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/tests/it-xsp-jakartaee/src/test/java/org/helmetsrequired/jacocotogo/JaCoCoToGo.java"&gt;forking the main utility class&lt;/a&gt;, trimming out the parts I didn't need (like JMX), switching it to NIO, and &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/blob/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/tests/it-xsp-jakartaee/src/test/java/it/org/openntf/xsp/jakartaee/docker/DominoContainer.java#L253"&gt;going from there&lt;/a&gt;.&lt;/p&gt;
  361. &lt;h3&gt;Using the Data&lt;/h3&gt;
  362. &lt;p&gt;With that all in place, a test run will end up with a file named &amp;quot;jacoco.exec&amp;quot; inside the &amp;quot;target&amp;quot; directory. Using this file varies by IDE, but, in Eclipse, you can install the &lt;a href="https://www.eclemma.org/"&gt;EclEmma&lt;/a&gt; tool, open the &amp;quot;Coverage&amp;quot; view, right-click in the table area, and choose &amp;quot;Import Session...&amp;quot;. That will let you locate the file and then choose the projects from your workspace that you're looking to analyze.&lt;/p&gt;
  363. &lt;p&gt;When I did that, I got my results:&lt;/p&gt;
  364. &lt;img src="media/DC11764D3186437889923B427FA275F1/coverage-view-results.png" alt="Screenshot of Eclipse&amp;#39;s Coverage tool detailing my test suite&amp;#39;s coverage of somewhere around 50-65%" title="Screenshot of Eclipse&amp;#39;s Coverage tool detailing my test suite&amp;#39;s coverage of somewhere around 50-65%" border="0" width="845" height="548" /&gt;
  365. &lt;p&gt;This is surprisingly good for the project, especially when you consider how large chunks of the red bars are things like the &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/tree/57e46852ee0c7ea49e02d73ebb2d8dbd9d2bfd18/eclipse/bundles/org.openntf.xsp.jakartaee.commons/src/org/openntf/xsp/jakartaee/servlet"&gt;servlet wrapper package&lt;/a&gt;, which includes a lot of delegating code that is obligatory to match the interface but is not likely to be actually used in practice.&lt;/p&gt;
  366. &lt;p&gt;While this is currently the only project where I've needed to do this, it'll certainly be good to keep these techniques in mind. The TCP port thing in particular should be handy in future edge cases even without the Docker part.&lt;/p&gt;
  367. </description></item><item><title>Homelab Rework: Phase 3 - TrueNAS Core to Scale</title><link>https://frostillic.us/blog/posts/2024/3/2/homelab-rework-phase-3-truenas-core-to-scale</link><content:encoded>&lt;p&gt;When I last &lt;a href="/blog/posts/2023/9/15/homelab-rework-phase-2"&gt;talked about the ragtag fleet of computers I generously call a &amp;quot;homelab&amp;quot; now&lt;/a&gt;, I had converted my gaming/VM machine back from Proxmox to Windows, where it remains (successfully) to this day.&lt;/p&gt;
  368. &lt;p&gt;For a &lt;a href="/blog/posts/2023/6/25/planning-a-homelab-rework"&gt;while&lt;/a&gt;, though, I've been eyeing converting my NAS from TrueNAS Core to Scale. While I really like FreeBSD technically and philosophically, running Linux was very appealing for a number of reasons. Still, it was a high-risk operation, even though &lt;a href="https://www.truenas.com/docs/scale/gettingstarted/migrate/migratingfromcore/"&gt;the actual process of migration&lt;/a&gt; looked almost impossibly easy. For some reason, I decided to take the plunge this week.&lt;/p&gt;
  369. &lt;h2&gt;The Setup&lt;/h2&gt;
  370. &lt;p&gt;Before going into the actual process, I'll describe the setup a bit. The machine in question is a &lt;a href="https://everymac.com/systems/apple/mac_pro/specs/mac-pro-quad-2.66-specs.html"&gt;Mac Pro 1,1&lt;/a&gt;: two Xeon 5150s, four traditional HDDs for storage, and a handful of M.2 drives. The machine itself is far, far too old to have NVMe on the motherboard, but it &lt;em&gt;does&lt;/em&gt; have PCIe, so I got a couple adapter cards. The boot volume is a SATA M.2 disk on one of them, while I have some actual NVMe ones serving as cache/log devices in the ZFS pool. Also, though everything says that the maximum RAM capacity is 32 GB, I actually have 64 in there and it's worked perfectly.&lt;/p&gt;
  371. &lt;p&gt;It's a bit of a weird beast this way, but those old Mac Pros were built to last, and it's holding up.&lt;/p&gt;
  372. &lt;p&gt;Also, if you're not familiar with &lt;a href="https://en.wikipedia.org/wiki/TrueNAS"&gt;TrueNAS&lt;/a&gt; and its different variants, it's worth a bit of explanation. TrueNAS Core (née FreeNAS) is a FreeBSD-based NAS-focused OS. You primarily interact with it via a web-based GUI and its various features heavily revolve around the use of &lt;a href="https://en.wikipedia.org/wiki/ZFS"&gt;ZFS&lt;/a&gt;, while its app system uses &lt;a href="https://en.wikipedia.org/wiki/FreeBSD_jail"&gt;FreeBSD jails&lt;/a&gt; and its VM system uses &lt;a href="https://en.wikipedia.org/wiki/Bhyve"&gt;Bhyve&lt;/a&gt;. TrueNAS Scale is a related system, but based on Debian Linux instead of FreeBSD. It still uses ZFS, and its GUI is similar to Core, but it implements its apps and VMs differently (more on this in a bit). For NAS/file-share uses, there's actually less of a difference than you might think based on their different underlying OSes, but the distinctions come into play once you go beyond the basics.&lt;/p&gt;
  373. &lt;h2&gt;The Conversion&lt;/h2&gt;
  374. &lt;p&gt;If anything, the above-linked documentation &lt;em&gt;overstates&lt;/em&gt; the complexity of the operation. I didn't even need to go the &amp;quot;manual update&amp;quot; route: I went to the Update panel, switched from the TrueNAS Core train to the current non-beta TrueNAS Scale one, hit Update, and let it go. It took a long time, presumably due to the age of the machine, but it did its job and came back up on its own.&lt;/p&gt;
  375. &lt;p&gt;Well, mostly: for some reason, the actual data ZFS pool was sort of half-detached. The OS knew it was supposed to have a pool by its name, but didn't match it up to the existing disks. To fix this, I deleted the configuration for the pool (but did &lt;em&gt;not&lt;/em&gt; delete the connected service configuration) and then went to Import Pool, where the real one existed. Once it was imported, everything lined back up without further issue.&lt;/p&gt;
  376. &lt;p&gt;Being basically a completely-different OS, there are &lt;a href="https://www.truenas.com/docs/scale/gettingstarted/migrate/migrateprep/"&gt;a number of features that Core supports but Scale doesn't&lt;/a&gt;. Of that list, the only one I was using was the plugin/jail system, but I had whittled my use down to just Postgres (containing only discardable dev data) and Plex. These are both readily available in Scale's app system, and it was quick enough to get Plex re-set-up with the same library data.&lt;/p&gt;
  377. &lt;h2&gt;Apps&lt;/h2&gt;
  378. &lt;p&gt;As I mentioned, TrueNAS Core uses a custom-built &amp;quot;plugin&amp;quot; system sitting on top of the venerable FreeBSD jail capabilities. Those jails are similar in concept to things like Docker containers, and work very similarly in practice to the &lt;a href="https://en.wikipedia.org/wiki/LXC"&gt;Linux Containers&lt;/a&gt; system I experienced with Proxmox.&lt;/p&gt;
  379. &lt;p&gt;TrueNAS Scale, for its part, uses &lt;a href="https://en.wikipedia.org/wiki/Kubernetes"&gt;Kubernetes&lt;/a&gt;, specifically by way of &lt;a href="https://k3s.io/"&gt;K3s&lt;/a&gt;, and provides its own convenient UI on top of it. Good thing it &lt;em&gt;does&lt;/em&gt; provide this UI, too, since Kubernetes is a whole freaking thing, and I've up until this point stayed away from learning it. I guess my time has come, though. Kubernetes is distinct from Docker - while older versions used Docker as a runtime of sorts, this was always an implementation detail, and the system in use in current TrueNAS Scale is &lt;a href="https://en.wikipedia.org/wiki/Cloud_Native_Computing_Foundation#containerd"&gt;&lt;code&gt;containerd&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
  380. &lt;p&gt;Setting aside the conceptual complexity of Kubernetes, this distinction from Core is handy: while not being Docker, Kubernetes can consume Docker-compatible images and run them, and &lt;em&gt;that&lt;/em&gt; ecosystem is &lt;em&gt;huge&lt;/em&gt;. Additionally, while TrueNAS ships with a set of common app &amp;quot;charts&amp;quot; (Plex included), there's a community project named &lt;a href="https://github.com/truecharts/charts"&gt;TrueCharts&lt;/a&gt; that adds definitions for tons and tons more.&lt;/p&gt;
  381. &lt;h2&gt;Domino&lt;/h2&gt;
  382. &lt;p&gt;That brings me to our beloved Domino. I had actually kind of gotten Domino running in a jail on TrueNAS Core, but it was much more an exercise in seeing if I could do it than anything useful: the installer didn't run, so I had to copy an installation from elsewhere, and the JVM wouldn't even load up without crashing. Neat to see, but I didn't keep it around.&lt;/p&gt;
  383. &lt;p&gt;The prospect on Scale is better, though. For one, it's actually Linux and thus doesn't need a binary-compatibility shim &lt;a href="https://docs.freebsd.org/en/books/handbook/linuxemu/"&gt;like FreeBSD has&lt;/a&gt;, and the container runtime meant I could presumably just use the normal &lt;a href="https://github.com/HCL-TECH-SOFTWARE/domino-container"&gt;image-building process&lt;/a&gt;. I could also run it in a VM, since the Linux hypervisor works on this machine while bhyve did not, but I figured I'd give the container path a shot.&lt;/p&gt;
  384. &lt;p&gt;Before I go any further, I'll give a huge caveat: while this works better than running it on FreeBSD, I wouldn't recommend actually doing what I've done for production. It'll presumably do what I want it to do here (be a local replica of all of my DBs without requiring a distinct VM), it's not ideal. For one, Domino plus Kubernetes is a weird mix: Kubernetes is all about building up and tearing down a swarm of containers dynamically, while Domino is much more of a single-server sort of thing. It works, certainly, but Kubernetes is always there to tempt you into doing things weird. Also, I know almost nothing about Kubernetes anyway, so don't take anything I say here as advice. It's good fun, though.&lt;/p&gt;
  385. &lt;p&gt;That said, on to the specifics!&lt;/p&gt;
  386. &lt;h4&gt;Deploying the Container&lt;/h4&gt;
  387. &lt;p&gt;The way the TrueNAS app UI works, you can go to &amp;quot;Custom App&amp;quot; and configure your container by referencing a Docker image from a repository. I don't normally actually host a Docker registry, instead manually &lt;a href="https://help.hcltechsw.com/domino/14.0.0/admin/inst_dock_load_tar_archive.html"&gt;loading the image into the runtime&lt;/a&gt;. It might be possible to do that here, but I took the opportunity to set up a quick local-network-only one on my other machine, both because I figured it'd be neat to learn to do that and because I forgot about the Harbor-hosted option on that link.&lt;/p&gt;
  388. &lt;p&gt;Since the local registry used HTTP and there's nowhere in the TrueNAS UI to tell it to not use HTTPS, I followed &lt;a href="https://www.truenas.com/community/threads/allow-insecure-docker-registries.113927/"&gt;this suggestion&lt;/a&gt; to configure K3s to explicitly map it. With that in place, I was able to start pulling images from my registry.&lt;/p&gt;
  389. &lt;h4&gt;The Domino Version&lt;/h4&gt;
  390. &lt;p&gt;One quirk I quickly ran into was that I can't use Domino 14 on here. The reason for this isn't an OS problem, but rather a hardware limitation: the new glibc that Domino 14 uses requires &lt;a href="https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels"&gt;the &amp;quot;x86-64-v2&amp;quot; microarchitecture level&lt;/a&gt; and the Xeon 5150 just doesn't have that by dint of pre-dating it by two years.&lt;/p&gt;
  391. &lt;p&gt;That's fine, though: I really just want this to house data, not app development, and 12.0.2 will do that with aplomb.&lt;/p&gt;
  392. &lt;h4&gt;Volume Configuration&lt;/h4&gt;
  393. &lt;p&gt;The way I usually set up a Domino container when using e.g. Docker Compose is that I define a handful of volumes to go with it: for the normal data dir, for DAOS, for the Transaction Log, and so forth. This is a bit of an affectation, I suppose, since I could also just define one volume for everything and it's not like I actually host these volumes elsewhere, but... I don't know, I like it. It keeps things disciplined.&lt;/p&gt;
  394. &lt;p&gt;Anyway, I originally set this up equivalently in the Custom App UI in TrueNAS, creating a &amp;quot;Volume&amp;quot; entry for each of these. However, I found that, for some reason, Domino didn't have write access to the newly-created volumes. Maybe this is due to the uid the container is built to use or something, but I worked around it by using Host Path Volumes instead. The net effect is the same, since they're in the same ZFS pool, and this actually makes it easier to peek at the data anyway, since it can be in the SMB share.&lt;/p&gt;
  395. &lt;p&gt;Once I did that and made sure the container user could modify the data, all was well. Mostly, anyway.&lt;/p&gt;
  396. &lt;h4&gt;Transaction Logs, ZFS, and Sector Size&lt;/h4&gt;
  397. &lt;p&gt;Once Domino got going, I noticed a minor problem: it would crash quickly, every time. Specifically, it crashed when it started preparing the transaction log directory. I eventually remembered running into the same problem on Proxmox at one point, and it brought me back to &lt;a href="https://tednote.com/post/2020-12-03-domino-transaction-logs-4k-sectors/"&gt;this blog post by Ted Hardenburgh&lt;/a&gt;. Long story short, my ZFS pool uses 4K sectors and Domino's transaction logs can't deal with that, at least in 12.0.2 and below.&lt;/p&gt;
  398. &lt;p&gt;This put me in a bit of a sticky spot, since the way to change this is to &lt;a href="https://unix.stackexchange.com/a/90636"&gt;re-create the entire pool&lt;/a&gt; and I really didn't want to do that.&lt;/p&gt;
  399. &lt;p&gt;I came up with a workaround, though, in the form of making a little disk image and formatting it &lt;a href="https://en.wikipedia.org/wiki/Ext4"&gt;ext4&lt;/a&gt;. You can use a &lt;a href="https://en.wikipedia.org/wiki/Loop_device"&gt;loop device&lt;/a&gt; to mount a file like a disk, so the process looks like this:&lt;/p&gt;
  400. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1
  401. 2
  402. 3
  403. 4&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;dd &lt;span style="color: #008800; font-weight: bold"&gt;if&lt;/span&gt;&lt;span style="color: #333333"&gt;=&lt;/span&gt;/dev/zero &lt;span style="color: #996633"&gt;of&lt;/span&gt;&lt;span style="color: #333333"&gt;=&lt;/span&gt;tlog.img &lt;span style="color: #996633"&gt;bs&lt;/span&gt;&lt;span style="color: #333333"&gt;=&lt;/span&gt;1G &lt;span style="color: #996633"&gt;count&lt;/span&gt;&lt;span style="color: #333333"&gt;=&lt;/span&gt;1
  404. sudo /sbin/losetup --find --show tlog.img
  405. sudo mkfs.ext4 /dev/loop0
  406. sudo mount /dev/loop0 /mnt/tlog
  407. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  408. &lt;/div&gt;
  409. &lt;p&gt;That makes a 1GB disk image, formats it ext4, and mounts it as &amp;quot;/mnt/tlog&amp;quot;. This process defaults to 512-byte sectors, so I made a directory within it writable by the container user (more on this shortly), configured the Domino container to map the transaction log directory to that path, and all was well.&lt;/p&gt;
  410. &lt;p&gt;Normally, to get this mounted at boot, you'd likely put an entry in &lt;a href="https://en.wikipedia.org/wiki/Fstab"&gt;fstab&lt;/a&gt;. However, TrueNAS assumes control over system configuration files like that, and you shouldn't edit them directly. Instead, what I did was write a small script that does the &lt;code&gt;losetup&lt;/code&gt; and &lt;code&gt;mount&lt;/code&gt; lines above and added an entry in &amp;quot;System Settings&amp;quot; - &amp;quot;Advanced&amp;quot; - &amp;quot;Init/Shutdown Scripts&amp;quot; to run this at pre-init.&lt;/p&gt;
  411. &lt;h4&gt;Networking&lt;/h4&gt;
  412. &lt;p&gt;The next hurdle I wanted to get over was the networking side. You can map ports in apps in a similar way to what you'd do with Docker, but &lt;a href="https://www.truenas.com/community/threads/containers-minimum-node-port-9000.90679/"&gt;you have to map them to a port 9000 or above&lt;/a&gt;. That would be an annoying issue in general, but especially for NRPC. Fortunately, the app configuration allows you give the container its own IP address in the &amp;quot;Add external Interfaces&amp;quot; (sic) configuration section. Since the virtual MAC address changes each time the container is deployed, I gave it a static IP address matching a reservation I carved out on my DHCP server, pointed it to the DNS server, and all was well. All of Domino's open ports are available on that IP, and it's basically like a VM in that way.&lt;/p&gt;
  413. &lt;h4&gt;Container User&lt;/h4&gt;
  414. &lt;p&gt;Normally, containers in TrueNAS's app system run as the &amp;quot;apps&amp;quot; user, though this is configurable per-app. The way the Domino container launches, though, it runs as UID 1000, which is &lt;code&gt;notes&lt;/code&gt; inside the container. &lt;em&gt;Outside&lt;/em&gt; the container, on my setup, that ID maps to... my user account &lt;code&gt;jesse&lt;/code&gt;.&lt;/p&gt;
  415. &lt;p&gt;Administration-wise, that's not exactly the best! In a less &amp;quot;for fun&amp;quot; situation, I'd change the container user or look into UID mapping as I've done with Docker in the past, but honestly it's fine here. This means it's easy for me to access and edit Domino data/config files over the share, and it made the volume mapping above work without incident. As long as no admins find out about this, it can be &lt;a href="https://www.youtube.com/watch?v=8CRHFfb_mDY"&gt;my secret shame&lt;/a&gt;.&lt;/p&gt;
  416. &lt;h2&gt;Future Uses&lt;/h2&gt;
  417. &lt;p&gt;So, at this point, the server is doing the jobs it was doing previously, plus acting as a nice extra replica server for Domino. It's positioned well now for me to do a lot of other tinkering.&lt;/p&gt;
  418. &lt;p&gt;For one, it'll be a good opportunity for me to finally learn about Kubernetes, which I've been dragging my feet on. I installed the &lt;a href="https://www.portainer.io/"&gt;Portainer&lt;/a&gt; chart from TrueCharts to give me a look into the K8s layer in a way that's less abstracted than the TrueNAS UI but more familiar and comfortable than the &lt;code&gt;kubectl&lt;/code&gt; tool for me for now.&lt;/p&gt;
  419. &lt;p&gt;Additionally, since the hypervisor works on here, it'll be another good location for me to store utility VMs when I need them, rather than putting everything on the Windows machine (which has half as much RAM).&lt;/p&gt;
  420. &lt;p&gt;I could possibly use it to host servers for various games like Terraria, though I'm a bit wary of throwing such ancient processors at the task. We'll see about that.&lt;/p&gt;
  421. &lt;p&gt;In general, I want to try hosting more things from home when they're non-critical, and this will definitely give me the opportunity. It's also quite fun to tinker with, and that's the most important thing.&lt;/p&gt;
  422. </content:encoded><guid>912BEDAFD66840529AE071FD2680A1AE</guid><dc:creator></dc:creator><dc:date>2024-03-02T19:00:08Z</dc:date><pubDate>Sat, 2 Mar 2024 19:00:08 GMT</pubDate><description>&lt;p&gt;When I last &lt;a href="/blog/posts/2023/9/15/homelab-rework-phase-2"&gt;talked about the ragtag fleet of computers I generously call a &amp;quot;homelab&amp;quot; now&lt;/a&gt;, I had converted my gaming/VM machine back from Proxmox to Windows, where it remains (successfully) to this day.&lt;/p&gt;
  423. &lt;p&gt;For a &lt;a href="/blog/posts/2023/6/25/planning-a-homelab-rework"&gt;while&lt;/a&gt;, though, I've been eyeing converting my NAS from TrueNAS Core to Scale. While I really like FreeBSD technically and philosophically, running Linux was very appealing for a number of reasons. Still, it was a high-risk operation, even though &lt;a href="https://www.truenas.com/docs/scale/gettingstarted/migrate/migratingfromcore/"&gt;the actual process of migration&lt;/a&gt; looked almost impossibly easy. For some reason, I decided to take the plunge this week.&lt;/p&gt;
  424. &lt;h2&gt;The Setup&lt;/h2&gt;
  425. &lt;p&gt;Before going into the actual process, I'll describe the setup a bit. The machine in question is a &lt;a href="https://everymac.com/systems/apple/mac_pro/specs/mac-pro-quad-2.66-specs.html"&gt;Mac Pro 1,1&lt;/a&gt;: two Xeon 5150s, four traditional HDDs for storage, and a handful of M.2 drives. The machine itself is far, far too old to have NVMe on the motherboard, but it &lt;em&gt;does&lt;/em&gt; have PCIe, so I got a couple adapter cards. The boot volume is a SATA M.2 disk on one of them, while I have some actual NVMe ones serving as cache/log devices in the ZFS pool. Also, though everything says that the maximum RAM capacity is 32 GB, I actually have 64 in there and it's worked perfectly.&lt;/p&gt;
  426. &lt;p&gt;It's a bit of a weird beast this way, but those old Mac Pros were built to last, and it's holding up.&lt;/p&gt;
  427. &lt;p&gt;Also, if you're not familiar with &lt;a href="https://en.wikipedia.org/wiki/TrueNAS"&gt;TrueNAS&lt;/a&gt; and its different variants, it's worth a bit of explanation. TrueNAS Core (née FreeNAS) is a FreeBSD-based NAS-focused OS. You primarily interact with it via a web-based GUI and its various features heavily revolve around the use of &lt;a href="https://en.wikipedia.org/wiki/ZFS"&gt;ZFS&lt;/a&gt;, while its app system uses &lt;a href="https://en.wikipedia.org/wiki/FreeBSD_jail"&gt;FreeBSD jails&lt;/a&gt; and its VM system uses &lt;a href="https://en.wikipedia.org/wiki/Bhyve"&gt;Bhyve&lt;/a&gt;. TrueNAS Scale is a related system, but based on Debian Linux instead of FreeBSD. It still uses ZFS, and its GUI is similar to Core, but it implements its apps and VMs differently (more on this in a bit). For NAS/file-share uses, there's actually less of a difference than you might think based on their different underlying OSes, but the distinctions come into play once you go beyond the basics.&lt;/p&gt;
  428. &lt;h2&gt;The Conversion&lt;/h2&gt;
  429. &lt;p&gt;If anything, the above-linked documentation &lt;em&gt;overstates&lt;/em&gt; the complexity of the operation. I didn't even need to go the &amp;quot;manual update&amp;quot; route: I went to the Update panel, switched from the TrueNAS Core train to the current non-beta TrueNAS Scale one, hit Update, and let it go. It took a long time, presumably due to the age of the machine, but it did its job and came back up on its own.&lt;/p&gt;
  430. &lt;p&gt;Well, mostly: for some reason, the actual data ZFS pool was sort of half-detached. The OS knew it was supposed to have a pool by its name, but didn't match it up to the existing disks. To fix this, I deleted the configuration for the pool (but did &lt;em&gt;not&lt;/em&gt; delete the connected service configuration) and then went to Import Pool, where the real one existed. Once it was imported, everything lined back up without further issue.&lt;/p&gt;
  431. &lt;p&gt;Being basically a completely-different OS, there are &lt;a href="https://www.truenas.com/docs/scale/gettingstarted/migrate/migrateprep/"&gt;a number of features that Core supports but Scale doesn't&lt;/a&gt;. Of that list, the only one I was using was the plugin/jail system, but I had whittled my use down to just Postgres (containing only discardable dev data) and Plex. These are both readily available in Scale's app system, and it was quick enough to get Plex re-set-up with the same library data.&lt;/p&gt;
  432. &lt;h2&gt;Apps&lt;/h2&gt;
  433. &lt;p&gt;As I mentioned, TrueNAS Core uses a custom-built &amp;quot;plugin&amp;quot; system sitting on top of the venerable FreeBSD jail capabilities. Those jails are similar in concept to things like Docker containers, and work very similarly in practice to the &lt;a href="https://en.wikipedia.org/wiki/LXC"&gt;Linux Containers&lt;/a&gt; system I experienced with Proxmox.&lt;/p&gt;
  434. &lt;p&gt;TrueNAS Scale, for its part, uses &lt;a href="https://en.wikipedia.org/wiki/Kubernetes"&gt;Kubernetes&lt;/a&gt;, specifically by way of &lt;a href="https://k3s.io/"&gt;K3s&lt;/a&gt;, and provides its own convenient UI on top of it. Good thing it &lt;em&gt;does&lt;/em&gt; provide this UI, too, since Kubernetes is a whole freaking thing, and I've up until this point stayed away from learning it. I guess my time has come, though. Kubernetes is distinct from Docker - while older versions used Docker as a runtime of sorts, this was always an implementation detail, and the system in use in current TrueNAS Scale is &lt;a href="https://en.wikipedia.org/wiki/Cloud_Native_Computing_Foundation#containerd"&gt;&lt;code&gt;containerd&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
  435. &lt;p&gt;Setting aside the conceptual complexity of Kubernetes, this distinction from Core is handy: while not being Docker, Kubernetes can consume Docker-compatible images and run them, and &lt;em&gt;that&lt;/em&gt; ecosystem is &lt;em&gt;huge&lt;/em&gt;. Additionally, while TrueNAS ships with a set of common app &amp;quot;charts&amp;quot; (Plex included), there's a community project named &lt;a href="https://github.com/truecharts/charts"&gt;TrueCharts&lt;/a&gt; that adds definitions for tons and tons more.&lt;/p&gt;
  436. &lt;h2&gt;Domino&lt;/h2&gt;
  437. &lt;p&gt;That brings me to our beloved Domino. I had actually kind of gotten Domino running in a jail on TrueNAS Core, but it was much more an exercise in seeing if I could do it than anything useful: the installer didn't run, so I had to copy an installation from elsewhere, and the JVM wouldn't even load up without crashing. Neat to see, but I didn't keep it around.&lt;/p&gt;
  438. &lt;p&gt;The prospect on Scale is better, though. For one, it's actually Linux and thus doesn't need a binary-compatibility shim &lt;a href="https://docs.freebsd.org/en/books/handbook/linuxemu/"&gt;like FreeBSD has&lt;/a&gt;, and the container runtime meant I could presumably just use the normal &lt;a href="https://github.com/HCL-TECH-SOFTWARE/domino-container"&gt;image-building process&lt;/a&gt;. I could also run it in a VM, since the Linux hypervisor works on this machine while bhyve did not, but I figured I'd give the container path a shot.&lt;/p&gt;
  439. &lt;p&gt;Before I go any further, I'll give a huge caveat: while this works better than running it on FreeBSD, I wouldn't recommend actually doing what I've done for production. It'll presumably do what I want it to do here (be a local replica of all of my DBs without requiring a distinct VM), it's not ideal. For one, Domino plus Kubernetes is a weird mix: Kubernetes is all about building up and tearing down a swarm of containers dynamically, while Domino is much more of a single-server sort of thing. It works, certainly, but Kubernetes is always there to tempt you into doing things weird. Also, I know almost nothing about Kubernetes anyway, so don't take anything I say here as advice. It's good fun, though.&lt;/p&gt;
  440. &lt;p&gt;That said, on to the specifics!&lt;/p&gt;
  441. &lt;h4&gt;Deploying the Container&lt;/h4&gt;
  442. &lt;p&gt;The way the TrueNAS app UI works, you can go to &amp;quot;Custom App&amp;quot; and configure your container by referencing a Docker image from a repository. I don't normally actually host a Docker registry, instead manually &lt;a href="https://help.hcltechsw.com/domino/14.0.0/admin/inst_dock_load_tar_archive.html"&gt;loading the image into the runtime&lt;/a&gt;. It might be possible to do that here, but I took the opportunity to set up a quick local-network-only one on my other machine, both because I figured it'd be neat to learn to do that and because I forgot about the Harbor-hosted option on that link.&lt;/p&gt;
  443. &lt;p&gt;Since the local registry used HTTP and there's nowhere in the TrueNAS UI to tell it to not use HTTPS, I followed &lt;a href="https://www.truenas.com/community/threads/allow-insecure-docker-registries.113927/"&gt;this suggestion&lt;/a&gt; to configure K3s to explicitly map it. With that in place, I was able to start pulling images from my registry.&lt;/p&gt;
  444. &lt;h4&gt;The Domino Version&lt;/h4&gt;
  445. &lt;p&gt;One quirk I quickly ran into was that I can't use Domino 14 on here. The reason for this isn't an OS problem, but rather a hardware limitation: the new glibc that Domino 14 uses requires &lt;a href="https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels"&gt;the &amp;quot;x86-64-v2&amp;quot; microarchitecture level&lt;/a&gt; and the Xeon 5150 just doesn't have that by dint of pre-dating it by two years.&lt;/p&gt;
  446. &lt;p&gt;That's fine, though: I really just want this to house data, not app development, and 12.0.2 will do that with aplomb.&lt;/p&gt;
  447. &lt;h4&gt;Volume Configuration&lt;/h4&gt;
  448. &lt;p&gt;The way I usually set up a Domino container when using e.g. Docker Compose is that I define a handful of volumes to go with it: for the normal data dir, for DAOS, for the Transaction Log, and so forth. This is a bit of an affectation, I suppose, since I could also just define one volume for everything and it's not like I actually host these volumes elsewhere, but... I don't know, I like it. It keeps things disciplined.&lt;/p&gt;
  449. &lt;p&gt;Anyway, I originally set this up equivalently in the Custom App UI in TrueNAS, creating a &amp;quot;Volume&amp;quot; entry for each of these. However, I found that, for some reason, Domino didn't have write access to the newly-created volumes. Maybe this is due to the uid the container is built to use or something, but I worked around it by using Host Path Volumes instead. The net effect is the same, since they're in the same ZFS pool, and this actually makes it easier to peek at the data anyway, since it can be in the SMB share.&lt;/p&gt;
  450. &lt;p&gt;Once I did that and made sure the container user could modify the data, all was well. Mostly, anyway.&lt;/p&gt;
  451. &lt;h4&gt;Transaction Logs, ZFS, and Sector Size&lt;/h4&gt;
  452. &lt;p&gt;Once Domino got going, I noticed a minor problem: it would crash quickly, every time. Specifically, it crashed when it started preparing the transaction log directory. I eventually remembered running into the same problem on Proxmox at one point, and it brought me back to &lt;a href="https://tednote.com/post/2020-12-03-domino-transaction-logs-4k-sectors/"&gt;this blog post by Ted Hardenburgh&lt;/a&gt;. Long story short, my ZFS pool uses 4K sectors and Domino's transaction logs can't deal with that, at least in 12.0.2 and below.&lt;/p&gt;
  453. &lt;p&gt;This put me in a bit of a sticky spot, since the way to change this is to &lt;a href="https://unix.stackexchange.com/a/90636"&gt;re-create the entire pool&lt;/a&gt; and I really didn't want to do that.&lt;/p&gt;
  454. &lt;p&gt;I came up with a workaround, though, in the form of making a little disk image and formatting it &lt;a href="https://en.wikipedia.org/wiki/Ext4"&gt;ext4&lt;/a&gt;. You can use a &lt;a href="https://en.wikipedia.org/wiki/Loop_device"&gt;loop device&lt;/a&gt; to mount a file like a disk, so the process looks like this:&lt;/p&gt;
  455. &lt;div class="hilite-me"&gt;&lt;!-- HTML generated using hilite.me --&gt;&lt;div style="background: #ffffff; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;1
  456. 2
  457. 3
  458. 4&lt;/pre&gt;&lt;/td&gt;&lt;td&gt;&lt;pre style="margin: 0; line-height: 125%"&gt;dd &lt;span style="color: #008800; font-weight: bold"&gt;if&lt;/span&gt;&lt;span style="color: #333333"&gt;=&lt;/span&gt;/dev/zero &lt;span style="color: #996633"&gt;of&lt;/span&gt;&lt;span style="color: #333333"&gt;=&lt;/span&gt;tlog.img &lt;span style="color: #996633"&gt;bs&lt;/span&gt;&lt;span style="color: #333333"&gt;=&lt;/span&gt;1G &lt;span style="color: #996633"&gt;count&lt;/span&gt;&lt;span style="color: #333333"&gt;=&lt;/span&gt;1
  459. sudo /sbin/losetup --find --show tlog.img
  460. sudo mkfs.ext4 /dev/loop0
  461. sudo mount /dev/loop0 /mnt/tlog
  462. &lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
  463. &lt;/div&gt;
  464. &lt;p&gt;That makes a 1GB disk image, formats it ext4, and mounts it as &amp;quot;/mnt/tlog&amp;quot;. This process defaults to 512-byte sectors, so I made a directory within it writable by the container user (more on this shortly), configured the Domino container to map the transaction log directory to that path, and all was well.&lt;/p&gt;
  465. &lt;p&gt;Normally, to get this mounted at boot, you'd likely put an entry in &lt;a href="https://en.wikipedia.org/wiki/Fstab"&gt;fstab&lt;/a&gt;. However, TrueNAS assumes control over system configuration files like that, and you shouldn't edit them directly. Instead, what I did was write a small script that does the &lt;code&gt;losetup&lt;/code&gt; and &lt;code&gt;mount&lt;/code&gt; lines above and added an entry in &amp;quot;System Settings&amp;quot; - &amp;quot;Advanced&amp;quot; - &amp;quot;Init/Shutdown Scripts&amp;quot; to run this at pre-init.&lt;/p&gt;
  466. &lt;h4&gt;Networking&lt;/h4&gt;
  467. &lt;p&gt;The next hurdle I wanted to get over was the networking side. You can map ports in apps in a similar way to what you'd do with Docker, but &lt;a href="https://www.truenas.com/community/threads/containers-minimum-node-port-9000.90679/"&gt;you have to map them to a port 9000 or above&lt;/a&gt;. That would be an annoying issue in general, but especially for NRPC. Fortunately, the app configuration allows you give the container its own IP address in the &amp;quot;Add external Interfaces&amp;quot; (sic) configuration section. Since the virtual MAC address changes each time the container is deployed, I gave it a static IP address matching a reservation I carved out on my DHCP server, pointed it to the DNS server, and all was well. All of Domino's open ports are available on that IP, and it's basically like a VM in that way.&lt;/p&gt;
  468. &lt;h4&gt;Container User&lt;/h4&gt;
  469. &lt;p&gt;Normally, containers in TrueNAS's app system run as the &amp;quot;apps&amp;quot; user, though this is configurable per-app. The way the Domino container launches, though, it runs as UID 1000, which is &lt;code&gt;notes&lt;/code&gt; inside the container. &lt;em&gt;Outside&lt;/em&gt; the container, on my setup, that ID maps to... my user account &lt;code&gt;jesse&lt;/code&gt;.&lt;/p&gt;
  470. &lt;p&gt;Administration-wise, that's not exactly the best! In a less &amp;quot;for fun&amp;quot; situation, I'd change the container user or look into UID mapping as I've done with Docker in the past, but honestly it's fine here. This means it's easy for me to access and edit Domino data/config files over the share, and it made the volume mapping above work without incident. As long as no admins find out about this, it can be &lt;a href="https://www.youtube.com/watch?v=8CRHFfb_mDY"&gt;my secret shame&lt;/a&gt;.&lt;/p&gt;
  471. &lt;h2&gt;Future Uses&lt;/h2&gt;
  472. &lt;p&gt;So, at this point, the server is doing the jobs it was doing previously, plus acting as a nice extra replica server for Domino. It's positioned well now for me to do a lot of other tinkering.&lt;/p&gt;
  473. &lt;p&gt;For one, it'll be a good opportunity for me to finally learn about Kubernetes, which I've been dragging my feet on. I installed the &lt;a href="https://www.portainer.io/"&gt;Portainer&lt;/a&gt; chart from TrueCharts to give me a look into the K8s layer in a way that's less abstracted than the TrueNAS UI but more familiar and comfortable than the &lt;code&gt;kubectl&lt;/code&gt; tool for me for now.&lt;/p&gt;
  474. &lt;p&gt;Additionally, since the hypervisor works on here, it'll be another good location for me to store utility VMs when I need them, rather than putting everything on the Windows machine (which has half as much RAM).&lt;/p&gt;
  475. &lt;p&gt;I could possibly use it to host servers for various games like Terraria, though I'm a bit wary of throwing such ancient processors at the task. We'll see about that.&lt;/p&gt;
  476. &lt;p&gt;In general, I want to try hosting more things from home when they're non-critical, and this will definitely give me the opportunity. It's also quite fun to tinker with, and that's the most important thing.&lt;/p&gt;
  477. </description></item><item><title>XPages JEE 2.15.0 and Plans for JEE 10 and 11</title><link>https://frostillic.us/blog/posts/2024/2/16/xpages-jee-2-15-0-and-plans-for-jee-10-and-11</link><content:encoded>&lt;p&gt;Today, I released &lt;a href="https://openntf.org/main.nsf/project.xsp?r=project/XPages%20Jakarta%20EE%20Support/releases/56E3961911A6EDBA86258AC5006E21A4"&gt;version 2.15.0 of the XPages Jakarta EE project&lt;/a&gt;. As is often the case lately, this version contains bug fixes but also a few notable features:&lt;/p&gt;
  478. &lt;ul&gt;
  479. &lt;li&gt;You can now specify Servlets in WEB-INF/web.xml (as opposed to just via the &lt;code&gt;@WebServlet&lt;/code&gt; annotation. This is helpful for defining a Servlet when the actual implementation is in a JAR or when following non-annotation-based examples&lt;/li&gt;
  480. &lt;li&gt;You can now specify context-param values in WEB-INF/web.xml in the NSF and META-INF/web-fragment.xml in JAR design elements, which will be available to JSP, JSF, JAX-RS, &lt;code&gt;@WebServlet&lt;/code&gt;-annotated Servlets, and web.xml-defined Servlets&lt;/li&gt;
  481. &lt;li&gt;Added &lt;code&gt;@BooleanStorage&lt;/code&gt; annotation for NoSQL entities to define how boolean values are converted to note items&lt;/li&gt;
  482. &lt;li&gt;Added CRUD operations for calendar events to NoSQL, around a few new methods on Repository. This exposes some of the capabilities of NotesCalendar and can be used for, for example, providing an iCalendar feed based on a mail database. To go with that, XPages JEE also re-exports iCal4J as included in the Domino stack for NSF use, though this API is... not smooth&lt;/li&gt;
  483. &lt;/ul&gt;
  484. &lt;p&gt;The first two here are focused around bringing NSFs more in line with &amp;quot;normal&amp;quot; Jakarta EE applications, while the latter are some nice improvements for the NoSQL driver. I hope to put the last one in particular to good use - for example, &lt;a href="https://openntf.org"&gt;OpenNTF's site&lt;/a&gt; will be able to provide a calendar of webinars and other events that we can manage internally using a normal Notes calendar, and that sounds nice to me.&lt;/p&gt;
  485. &lt;h3&gt;Next Versions&lt;/h3&gt;
  486. &lt;p&gt;I still have the 3.x branch of the project chugging along, and I think it'll be ready for a real release before too long. Since it'll be a breaking-changes release thanks to upstream changes, I'm using it as an opportunity to consolidate the sprawl of features and XPages Libraries. Currently, my plan is:&lt;/p&gt;
  487. &lt;ul&gt;
  488. &lt;li&gt;One for &amp;quot;core&amp;quot;, covering most things in the &lt;a href="https://jakarta.ee/specifications/coreprofile/10/"&gt;Jakarta EE Core Profile&lt;/a&gt;, plus the other utility specs I've implemented: &lt;a href="https://jakarta.ee/specifications/transactions/2.0/"&gt;Transactions&lt;/a&gt;, &lt;a href="https://jakarta.ee/specifications/bean-validation/3.0/"&gt;Bean Validation&lt;/a&gt; (which really should be in Core in my estimation), &lt;a href="https://jakarta.ee/specifications/concurrency/3.0/"&gt;Concurrency&lt;/a&gt;, &lt;a href="https://jakarta.ee/specifications/servlet/6.0/"&gt;Servlet&lt;/a&gt;, and so forth, plus &lt;a href="https://jakarta.ee/specifications/data/1.0/"&gt;Data&lt;/a&gt; and &lt;a href="https://jakarta.ee/specifications/nosql/1.0/"&gt;NoSQL&lt;/a&gt;&lt;/li&gt;
  489. &lt;li&gt;One for &amp;quot;UI&amp;quot;, covering &lt;a href="https://jakarta.ee/specifications/pages/3.1/"&gt;Jakarta Pages&lt;/a&gt;, &lt;a href="https://jakarta.ee/specifications/faces/4.0/"&gt;Jakarta Faces&lt;/a&gt;, and &lt;a href="https://jakarta.ee/specifications/mvc/2.1/"&gt;MVC&lt;/a&gt; - basically, the stuff you could use to replace XPages to make an HTML-generating app in your NSF&lt;/li&gt;
  490. &lt;li&gt;One for &lt;a href="https://microprofile.io/"&gt;MicroProfile&lt;/a&gt;, or at least the specs I've implemented so far. I'm a little tempted to wrap this in to Core, since things like &lt;a href="https://microprofile.io/project/eclipse/microprofile-open-api"&gt;OpenAPI&lt;/a&gt; are useful almost all the time, but it's a clean-enough separation that it'll be fine&lt;/li&gt;
  491. &lt;/ul&gt;
  492. &lt;p&gt;This will require Domino 14, since Jakarta EE 10 requires at least Java 11.&lt;/p&gt;
  493. &lt;p&gt;That brings me to some unexpected good news: though Jakarta EE 11 was long planned to use Java 21 as its minimum version (since 21 is the current LTS), it looks like they've &lt;a href="https://jakartaee.github.io/platform/jakartaee11/JakartaEE11ReleasePlan"&gt;switched to making Java 17 the baseline&lt;/a&gt;. For me, this is a little sad in an idealistic sense, since it pushes things like &lt;a href="https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html"&gt;Virtual Threads&lt;/a&gt; out of the realm of being a core part of JEE, but I'm &lt;em&gt;very&lt;/em&gt; happy that I'll be able to use all JEE 11 specs in Domino 14. Even if Domino 15 used Java 21, it'd still be a long while before that came, and we'd lag behind the standard for at least a year. Instead, this puts the project back in line with upstream, and allows me personally to potentially resume committing to Jakarta NoSQL - I'd been out of the loop for a very long time when it moved to 11 and then 17 as its required version.&lt;/p&gt;
  494. &lt;p&gt;I don't know right now whether JEE 11 will be the same sort of breaking change for the project (which would mean a 4.x release) or if I'll be able to make it a 3.x one - the specs aren't out yet, so time will tell. The big focus of 11 will be further centralization on CDI instead of EJB, and I'm all for it.&lt;/p&gt;
  495. &lt;p&gt;My plan is to get 3.x out for Domino 14, based on JEE 10, as soon as time allowed, and then I'll start looking into bumping to JEE 11 when it releases in the summer.&lt;/p&gt;
  496. </content:encoded><guid>B94098777A32415894BB57FD0502E12D</guid><dc:creator></dc:creator><dc:date>2024-02-16T20:30:40Z</dc:date><pubDate>Fri, 16 Feb 2024 20:30:40 GMT</pubDate><description>&lt;p&gt;Today, I released &lt;a href="https://openntf.org/main.nsf/project.xsp?r=project/XPages%20Jakarta%20EE%20Support/releases/56E3961911A6EDBA86258AC5006E21A4"&gt;version 2.15.0 of the XPages Jakarta EE project&lt;/a&gt;. As is often the case lately, this version contains bug fixes but also a few notable features:&lt;/p&gt;
  497. &lt;ul&gt;
  498. &lt;li&gt;You can now specify Servlets in WEB-INF/web.xml (as opposed to just via the &lt;code&gt;@WebServlet&lt;/code&gt; annotation. This is helpful for defining a Servlet when the actual implementation is in a JAR or when following non-annotation-based examples&lt;/li&gt;
  499. &lt;li&gt;You can now specify context-param values in WEB-INF/web.xml in the NSF and META-INF/web-fragment.xml in JAR design elements, which will be available to JSP, JSF, JAX-RS, &lt;code&gt;@WebServlet&lt;/code&gt;-annotated Servlets, and web.xml-defined Servlets&lt;/li&gt;
  500. &lt;li&gt;Added &lt;code&gt;@BooleanStorage&lt;/code&gt; annotation for NoSQL entities to define how boolean values are converted to note items&lt;/li&gt;
  501. &lt;li&gt;Added CRUD operations for calendar events to NoSQL, around a few new methods on Repository. This exposes some of the capabilities of NotesCalendar and can be used for, for example, providing an iCalendar feed based on a mail database. To go with that, XPages JEE also re-exports iCal4J as included in the Domino stack for NSF use, though this API is... not smooth&lt;/li&gt;
  502. &lt;/ul&gt;
  503. &lt;p&gt;The first two here are focused around bringing NSFs more in line with &amp;quot;normal&amp;quot; Jakarta EE applications, while the latter are some nice improvements for the NoSQL driver. I hope to put the last one in particular to good use - for example, &lt;a href="https://openntf.org"&gt;OpenNTF's site&lt;/a&gt; will be able to provide a calendar of webinars and other events that we can manage internally using a normal Notes calendar, and that sounds nice to me.&lt;/p&gt;
  504. &lt;h3&gt;Next Versions&lt;/h3&gt;
  505. &lt;p&gt;I still have the 3.x branch of the project chugging along, and I think it'll be ready for a real release before too long. Since it'll be a breaking-changes release thanks to upstream changes, I'm using it as an opportunity to consolidate the sprawl of features and XPages Libraries. Currently, my plan is:&lt;/p&gt;
  506. &lt;ul&gt;
  507. &lt;li&gt;One for &amp;quot;core&amp;quot;, covering most things in the &lt;a href="https://jakarta.ee/specifications/coreprofile/10/"&gt;Jakarta EE Core Profile&lt;/a&gt;, plus the other utility specs I've implemented: &lt;a href="https://jakarta.ee/specifications/transactions/2.0/"&gt;Transactions&lt;/a&gt;, &lt;a href="https://jakarta.ee/specifications/bean-validation/3.0/"&gt;Bean Validation&lt;/a&gt; (which really should be in Core in my estimation), &lt;a href="https://jakarta.ee/specifications/concurrency/3.0/"&gt;Concurrency&lt;/a&gt;, &lt;a href="https://jakarta.ee/specifications/servlet/6.0/"&gt;Servlet&lt;/a&gt;, and so forth, plus &lt;a href="https://jakarta.ee/specifications/data/1.0/"&gt;Data&lt;/a&gt; and &lt;a href="https://jakarta.ee/specifications/nosql/1.0/"&gt;NoSQL&lt;/a&gt;&lt;/li&gt;
  508. &lt;li&gt;One for &amp;quot;UI&amp;quot;, covering &lt;a href="https://jakarta.ee/specifications/pages/3.1/"&gt;Jakarta Pages&lt;/a&gt;, &lt;a href="https://jakarta.ee/specifications/faces/4.0/"&gt;Jakarta Faces&lt;/a&gt;, and &lt;a href="https://jakarta.ee/specifications/mvc/2.1/"&gt;MVC&lt;/a&gt; - basically, the stuff you could use to replace XPages to make an HTML-generating app in your NSF&lt;/li&gt;
  509. &lt;li&gt;One for &lt;a href="https://microprofile.io/"&gt;MicroProfile&lt;/a&gt;, or at least the specs I've implemented so far. I'm a little tempted to wrap this in to Core, since things like &lt;a href="https://microprofile.io/project/eclipse/microprofile-open-api"&gt;OpenAPI&lt;/a&gt; are useful almost all the time, but it's a clean-enough separation that it'll be fine&lt;/li&gt;
  510. &lt;/ul&gt;
  511. &lt;p&gt;This will require Domino 14, since Jakarta EE 10 requires at least Java 11.&lt;/p&gt;
  512. &lt;p&gt;That brings me to some unexpected good news: though Jakarta EE 11 was long planned to use Java 21 as its minimum version (since 21 is the current LTS), it looks like they've &lt;a href="https://jakartaee.github.io/platform/jakartaee11/JakartaEE11ReleasePlan"&gt;switched to making Java 17 the baseline&lt;/a&gt;. For me, this is a little sad in an idealistic sense, since it pushes things like &lt;a href="https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html"&gt;Virtual Threads&lt;/a&gt; out of the realm of being a core part of JEE, but I'm &lt;em&gt;very&lt;/em&gt; happy that I'll be able to use all JEE 11 specs in Domino 14. Even if Domino 15 used Java 21, it'd still be a long while before that came, and we'd lag behind the standard for at least a year. Instead, this puts the project back in line with upstream, and allows me personally to potentially resume committing to Jakarta NoSQL - I'd been out of the loop for a very long time when it moved to 11 and then 17 as its required version.&lt;/p&gt;
  513. &lt;p&gt;I don't know right now whether JEE 11 will be the same sort of breaking change for the project (which would mean a 4.x release) or if I'll be able to make it a 3.x one - the specs aren't out yet, so time will tell. The big focus of 11 will be further centralization on CDI instead of EJB, and I'm all for it.&lt;/p&gt;
  514. &lt;p&gt;My plan is to get 3.x out for Domino 14, based on JEE 10, as soon as time allowed, and then I'll start looking into bumping to JEE 11 when it releases in the summer.&lt;/p&gt;
  515. </description></item><item><title>Notes/Domino 14 Fallout</title><link>https://frostillic.us/blog/posts/2023/12/15/notes-domino-14-fallout</link><content:encoded>&lt;p&gt;Notes and Domino 14 are out now and, as I &lt;a href="/blog/posts/2023/6/2/kicking-the-tires-on-domino-14-and-java-17"&gt;discussed back in June&lt;/a&gt; the big deal for me is the move to Java 17. This also came with a refresh of the Eclipse innards, from Neon (circa 2016) to 2021-12 (circa, uh, 2021). The Eclipse update is welcome, but so far it's been less impactful than the Java update - at some point, I'll want to see if some of the current-era Eclipse plugins work here, but that's for the future.&lt;/p&gt;
  516. &lt;p&gt;In the mean time, there's a bunch to know, so let's get to it! I've broken this one down into &amp;quot;critical&amp;quot; and &amp;quot;less critical&amp;quot; sections, since this post will likely have a similar life to my &lt;a href="/blog/posts/2018/10/19/abstractcompiledpage--missing-plugins--and-manifest.mf-in-fp10-and-v10"&gt;target platform one&lt;/a&gt;.&lt;/p&gt;
  517. &lt;h2&gt;Critical To Know&lt;/h2&gt;
  518. &lt;h4&gt;ndext&lt;/h4&gt;
  519. &lt;p&gt;I mentioned this in June, but an important thing to know about Java 17 is that &amp;quot;jvm/lib/ext&amp;quot; no longer exists. Fortunately, Notes and Domino have long had a secondary location for this sort of thing: &amp;quot;ndext&amp;quot; in the program directory. Any JARs in this folder are available at runtime on both platforms, and so the quick fix is to move anything you had in &amp;quot;jvm/lib/ext&amp;quot; to &amp;quot;ndext&amp;quot;.&lt;/p&gt;
  520. &lt;p&gt;...&lt;em&gt;but&lt;/em&gt;.&lt;/p&gt;
  521. &lt;p&gt;When it comes to Notes, there's a distinct difference between &amp;quot;available at runtime&amp;quot; and &amp;quot;on the build path&amp;quot;. While Java agents here are fine - they modified the editor to include &amp;quot;ndext&amp;quot; in the build path - there's no such accommodation for XPages. So far, the best workaround I've thought of is to add any such JARs manually to the JRE definition in Eclipse's preferences:&lt;/p&gt;
  522. &lt;img src="media/F6095739D34F44AE89327B0397C67041/designer-jre-def.png" alt="Screenshot of Designer&amp;#39;s JRE preferences, showing the addition of an ndext JAR" title="designer-jre-def.png" border="0" width="730" height="449" /&gt;
  523. &lt;p&gt;When doing this, there's a huge caveat: do not just add everything from this directory to the JRE. Most of it will be redundant, like the SWT stuff, but some will be actively harmful, in particular &amp;quot;jsdk.jar&amp;quot;. That's an even-older Servlet version than comes with XPages, and including that will cause Designer to think that methods added in Servlet 2 aren't present. Only add the JARs you're extending it with.&lt;/p&gt;
  524. &lt;p&gt;Ideally, this will be remedied one way or another in the future, but for now we'll have to do this to account for it. The silver lining may be that it's a good impetus to OSGi-ify your dependencies if possible.&lt;/p&gt;
  525. &lt;h4&gt;Poi Remains&lt;/h4&gt;
  526. &lt;p&gt;This isn't new, but it's worth mentioning while we're on the topic of &amp;quot;ndext&amp;quot;. Since Notes 11, the client ships with an old version of &lt;a href="https://poi.apache.org/"&gt;Apache Poi&lt;/a&gt;, but this is painfully distributed right in the JVM and not at the OSGi layer. Newer versions of &lt;a href="https://openntf.org/internal/home.nsf/project.xsp?action=openDocument&amp;amp;name=POI%204%20XPages"&gt;Poi 4 XPages&lt;/a&gt; deal with this, but it's important to know that, while Poi is in &lt;em&gt;Notes&lt;/em&gt;, it's not in &lt;em&gt;Domino&lt;/em&gt;. If you write agents using these classes, or add them to your JRE and use them in XPages, you'll also need to deploy them to Domino.&lt;/p&gt;
  527. &lt;h4&gt;Java Policy Location&lt;/h4&gt;
  528. &lt;p&gt;This one's more of a note, and it's reiterating something from June: the location of &amp;quot;java.policy&amp;quot; and (if you add it) &amp;quot;java.pol&amp;quot; changed since Java 8. They used to be in &amp;quot;jvm/lib/security&amp;quot; but they're in &amp;quot;jvm/conf/security&amp;quot; now. They work the same as before, as does putting a file named &amp;quot;.java.policy&amp;quot; in the Domino user's home dir, so the other characteristics haven't changed.&lt;/p&gt;
  529. &lt;h4&gt;sun.misc.BASE64Decoder&lt;/h4&gt;
  530. &lt;p&gt;&lt;a href="/blog/posts/2020/1/7/domino-11-s-java-switch-fallout"&gt;Back in Domino 11&lt;/a&gt;, the move from IBM J9 to OpenJ9 meant that some IBM internal forks of Sun internal classes were no longer available. The most notable of these were the BASE64 classes &lt;code&gt;com.ibm.misc.BASE64Encoder&lt;/code&gt; and &lt;code&gt;BASE64Decoder&lt;/code&gt;. These were very popular to use in the pre-Java-8 days, before &lt;code&gt;java.util.Base64&lt;/code&gt; existed, and so it was worth noting that they were gone then.&lt;/p&gt;
  531. &lt;p&gt;Well, &lt;code&gt;sun.misc.Base64Encoder&lt;/code&gt; has now met a similar fate - it's probably still in there somewhere, but it's no longer accessible by user code. If you haven't made the switch to &lt;code&gt;java.util.Base64&lt;/code&gt;, do so now.&lt;/p&gt;
  532. &lt;h4&gt;Target Platform Bug&lt;/h4&gt;
  533. &lt;p&gt;This is another note, but this classic bug that's been with us since 9.0.1FP10 is... fixed, I think! I'd thought at first that it remained, since the Target Platform config still just lists the Eclipse home dir, but installing and using an XPages library seemed to work properly without further change. Good!&lt;/p&gt;
  534. &lt;h4&gt;Java Compiler Level&lt;/h4&gt;
  535. &lt;p&gt;This is a fiddly one. Though Designer uses Java 17 by default now, it has the ability to compile down to older versions for past compatibility, such as running on a pre-14 server. Java agents have had &lt;a href="/blog/posts/2019/11/26/small-aside-writing-agents-with-java-5-features"&gt;their own way of dealing with this for a while&lt;/a&gt;, though it seems like maybe they default to Java 8 now, which is good. With XPages, it seems like it's a little less obvious.&lt;/p&gt;
  536. &lt;p&gt;From my experience, this is what I've found:&lt;/p&gt;
  537. &lt;ul&gt;
  538. &lt;li&gt;Existing projects may have a specific compiler setting specified (similar to agents in the above-linked post) and so will remain as Java 1.8&lt;/li&gt;
  539. &lt;li&gt;New projects seem to use the active workspace setting, which will matter&lt;/li&gt;
  540. &lt;li&gt;Upgrades of Designer in place will keep the default compiler level for the workspace at 1.8&lt;/li&gt;
  541. &lt;li&gt;A fresh installation sets the compiler level to 17&lt;/li&gt;
  542. &lt;/ul&gt;
  543. &lt;p&gt;This is all... fine. It'd be nice if it was a little more obvious to the developer (like if it was controlled by the xsp.properties setting for minimum version), but this is more or less the Eclipse way of doing things. We'll just have to be aware for a while of these settings and their interactions when developing for pre-14 deployment. If you're targeting an older server, you should make sure that you go to Project Properties for your NSF and set the Java Compiler level to fit:&lt;/p&gt;
  544. &lt;img src="media/31344FB6E14D4DB688FDA745288E8205/nsf-compiler.png" alt="Screenshot of the Java Compiler settings for an NSF" title="nsf-compiler.png" border="0" width="674" height="214" /&gt;
  545. &lt;h2&gt;Less Critical&lt;/h2&gt;
  546. &lt;p&gt;Alright, that covers the big-ticket stuff, but there are a few more things to know.&lt;/p&gt;
  547. &lt;h4&gt;Java Deprecation Warning On HTTP Start&lt;/h4&gt;
  548. &lt;p&gt;This one is documented, though oddly &lt;a href="https://help.hcltechsw.com/domino/14.0.0/admin/wn_components_no_longer_included_in_release.html"&gt;in the &amp;quot;no longer included&amp;quot; page&lt;/a&gt;: when you start HTTP on Domino, you'll see a message like this:&lt;/p&gt;
  549. &lt;pre&gt;&lt;code&gt;WARNING: A terminally deprecated method in java.lang.System has been called
  550. WARNING: System::setSecurityManager has been called by lotus.notes.AgentSecurityManager (file:/C:/Domino/ndext/Notes.jar)
  551. WARNING: Please consider reporting this to the maintainers of lotus.notes.AgentSecurityManager
  552. WARNING: System::setSecurityManager will be removed in a future release
  553. &lt;/code&gt;&lt;/pre&gt;
  554. &lt;p&gt;This is actually totally fine. As it says, the whole SecurityManager apparatus is gone in future versions, but Notes and Domino still use it for agents (and, unfortunately, XPages). While I long for the day when I never have to think about it again, this is a reasonable-enough compromise for 14 as a &amp;quot;transitional&amp;quot; version in its Java journey. So... you can ignore this and not worry.&lt;/p&gt;
  555. &lt;h4&gt;&amp;quot;Java Main Sources&amp;quot; and &amp;quot;Java Test Sources&amp;quot; Working Sets&lt;/h4&gt;
  556. &lt;p&gt;If you use Working Sets in Designer, you may notice two entries not of your creation:&lt;/p&gt;
  557. &lt;img src="media/F5C1B15295674767A01C403156176B5E/designer-working-sets.png" alt="Screenshot of the &amp;#39;Select Working Set&amp;#39; dialog in Designer 14" title="designer-working-sets.png" border="0" width="453" height="129" /&gt;
  558. &lt;p&gt;These showed up in Eclipse somewhere along the line, presumably to make it easier for people to select those types of projects without manually managing their working sets. Designer inherits this and HCL didn't remove them, but you're free to delete them if you want.&lt;/p&gt;
  559. &lt;h4&gt;&amp;quot;AbstractCompiledPage cannot be resolved&amp;quot;&lt;/h4&gt;
  560. &lt;p&gt;This is another one that came up back in 9.0.1FP10 and it's still here, but it's less of an issue: every once in a while, when building a project, Designer will complain about the &lt;code&gt;AbstractCompiledPage&lt;/code&gt; class not being found. In my experience, this only shows up temporarily during a build, but it's worth noting that, if it &lt;em&gt;does&lt;/em&gt; stick around for you, a Project - Clean should fix it.&lt;/p&gt;
  561. &lt;h4&gt;JAX-B and CORBA&lt;/h4&gt;
  562. &lt;p&gt;After Java 8, a few Java EE components were removed from the normal JRE distribution in favor of developing them in Java EE, then Jakarta EE. JAX-B is one of them (now &lt;a href="https://jakarta.ee/specifications/xml-binding/"&gt;Jakarta XML Binding&lt;/a&gt;) - we don't normally use this directly, but it comes up sometimes, either directly (likely as one of the other old-timey BASE64 workarounds) or as a dependency. It shouldn't matter in Designer, but, if you're writing Domino-targeting code outside Designer, you may need to be aware. One way or another, you should add this as a dependency - in a basic case, you can add &amp;quot;jakarta.xml.bind-api.jar&amp;quot; and &amp;quot;jaxb-impl.jar&amp;quot; from &amp;quot;ndext&amp;quot; to your build path.&lt;/p&gt;
  563. &lt;p&gt;Similarly, CORBA support classes (&amp;quot;org.omg&amp;quot; stuff, usually) used to come with the JRE and now don't. To work around this, HCL did what I've done in the past and included &amp;quot;glassfish-corba-omgapi.jar&amp;quot;, in their case putting it in &amp;quot;ndext&amp;quot;. If you're using Notes.jar with Java &amp;gt; 8 outside Designer, you'll need this one too.&lt;/p&gt;
  564. &lt;h2&gt;Conclusion&lt;/h2&gt;
  565. &lt;p&gt;I think that covers it for now. Considering how much changed with Java from 8 to 17, this could have been a lot rougher, though I fear that some of these workarounds will plague us for a long time.&lt;/p&gt;
  566. &lt;p&gt;In the mean time, I'd appreciate it if you vote for &lt;a href="https://domino-ideas.hcltechsw.com/ideas/DOMINO-I-2594"&gt;this Aha idea&lt;/a&gt; to keep Domino on a good Java update cadence. It'd be a shame if we sit on 17 right up until the very end of its life, as we did with 6 and 8, in part since these multi-version moves are more painful than single-LTS updates.&lt;/p&gt;
  567. </content:encoded><guid>7E490450A77E4A0F8DAD0F70B7DDE79D</guid><dc:creator></dc:creator><dc:date>2023-12-15T16:53:44Z</dc:date><pubDate>Fri, 15 Dec 2023 16:53:44 GMT</pubDate><description>&lt;p&gt;Notes and Domino 14 are out now and, as I &lt;a href="/blog/posts/2023/6/2/kicking-the-tires-on-domino-14-and-java-17"&gt;discussed back in June&lt;/a&gt; the big deal for me is the move to Java 17. This also came with a refresh of the Eclipse innards, from Neon (circa 2016) to 2021-12 (circa, uh, 2021). The Eclipse update is welcome, but so far it's been less impactful than the Java update - at some point, I'll want to see if some of the current-era Eclipse plugins work here, but that's for the future.&lt;/p&gt;
  568. &lt;p&gt;In the mean time, there's a bunch to know, so let's get to it! I've broken this one down into &amp;quot;critical&amp;quot; and &amp;quot;less critical&amp;quot; sections, since this post will likely have a similar life to my &lt;a href="/blog/posts/2018/10/19/abstractcompiledpage--missing-plugins--and-manifest.mf-in-fp10-and-v10"&gt;target platform one&lt;/a&gt;.&lt;/p&gt;
  569. &lt;h2&gt;Critical To Know&lt;/h2&gt;
  570. &lt;h4&gt;ndext&lt;/h4&gt;
  571. &lt;p&gt;I mentioned this in June, but an important thing to know about Java 17 is that &amp;quot;jvm/lib/ext&amp;quot; no longer exists. Fortunately, Notes and Domino have long had a secondary location for this sort of thing: &amp;quot;ndext&amp;quot; in the program directory. Any JARs in this folder are available at runtime on both platforms, and so the quick fix is to move anything you had in &amp;quot;jvm/lib/ext&amp;quot; to &amp;quot;ndext&amp;quot;.&lt;/p&gt;
  572. &lt;p&gt;...&lt;em&gt;but&lt;/em&gt;.&lt;/p&gt;
  573. &lt;p&gt;When it comes to Notes, there's a distinct difference between &amp;quot;available at runtime&amp;quot; and &amp;quot;on the build path&amp;quot;. While Java agents here are fine - they modified the editor to include &amp;quot;ndext&amp;quot; in the build path - there's no such accommodation for XPages. So far, the best workaround I've thought of is to add any such JARs manually to the JRE definition in Eclipse's preferences:&lt;/p&gt;
  574. &lt;img src="media/F6095739D34F44AE89327B0397C67041/designer-jre-def.png" alt="Screenshot of Designer&amp;#39;s JRE preferences, showing the addition of an ndext JAR" title="designer-jre-def.png" border="0" width="730" height="449" /&gt;
  575. &lt;p&gt;When doing this, there's a huge caveat: do not just add everything from this directory to the JRE. Most of it will be redundant, like the SWT stuff, but some will be actively harmful, in particular &amp;quot;jsdk.jar&amp;quot;. That's an even-older Servlet version than comes with XPages, and including that will cause Designer to think that methods added in Servlet 2 aren't present. Only add the JARs you're extending it with.&lt;/p&gt;
  576. &lt;p&gt;Ideally, this will be remedied one way or another in the future, but for now we'll have to do this to account for it. The silver lining may be that it's a good impetus to OSGi-ify your dependencies if possible.&lt;/p&gt;
  577. &lt;h4&gt;Poi Remains&lt;/h4&gt;
  578. &lt;p&gt;This isn't new, but it's worth mentioning while we're on the topic of &amp;quot;ndext&amp;quot;. Since Notes 11, the client ships with an old version of &lt;a href="https://poi.apache.org/"&gt;Apache Poi&lt;/a&gt;, but this is painfully distributed right in the JVM and not at the OSGi layer. Newer versions of &lt;a href="https://openntf.org/internal/home.nsf/project.xsp?action=openDocument&amp;amp;name=POI%204%20XPages"&gt;Poi 4 XPages&lt;/a&gt; deal with this, but it's important to know that, while Poi is in &lt;em&gt;Notes&lt;/em&gt;, it's not in &lt;em&gt;Domino&lt;/em&gt;. If you write agents using these classes, or add them to your JRE and use them in XPages, you'll also need to deploy them to Domino.&lt;/p&gt;
  579. &lt;h4&gt;Java Policy Location&lt;/h4&gt;
  580. &lt;p&gt;This one's more of a note, and it's reiterating something from June: the location of &amp;quot;java.policy&amp;quot; and (if you add it) &amp;quot;java.pol&amp;quot; changed since Java 8. They used to be in &amp;quot;jvm/lib/security&amp;quot; but they're in &amp;quot;jvm/conf/security&amp;quot; now. They work the same as before, as does putting a file named &amp;quot;.java.policy&amp;quot; in the Domino user's home dir, so the other characteristics haven't changed.&lt;/p&gt;
  581. &lt;h4&gt;sun.misc.BASE64Decoder&lt;/h4&gt;
  582. &lt;p&gt;&lt;a href="/blog/posts/2020/1/7/domino-11-s-java-switch-fallout"&gt;Back in Domino 11&lt;/a&gt;, the move from IBM J9 to OpenJ9 meant that some IBM internal forks of Sun internal classes were no longer available. The most notable of these were the BASE64 classes &lt;code&gt;com.ibm.misc.BASE64Encoder&lt;/code&gt; and &lt;code&gt;BASE64Decoder&lt;/code&gt;. These were very popular to use in the pre-Java-8 days, before &lt;code&gt;java.util.Base64&lt;/code&gt; existed, and so it was worth noting that they were gone then.&lt;/p&gt;
  583. &lt;p&gt;Well, &lt;code&gt;sun.misc.Base64Encoder&lt;/code&gt; has now met a similar fate - it's probably still in there somewhere, but it's no longer accessible by user code. If you haven't made the switch to &lt;code&gt;java.util.Base64&lt;/code&gt;, do so now.&lt;/p&gt;
  584. &lt;h4&gt;Target Platform Bug&lt;/h4&gt;
  585. &lt;p&gt;This is another note, but this classic bug that's been with us since 9.0.1FP10 is... fixed, I think! I'd thought at first that it remained, since the Target Platform config still just lists the Eclipse home dir, but installing and using an XPages library seemed to work properly without further change. Good!&lt;/p&gt;
  586. &lt;h4&gt;Java Compiler Level&lt;/h4&gt;
  587. &lt;p&gt;This is a fiddly one. Though Designer uses Java 17 by default now, it has the ability to compile down to older versions for past compatibility, such as running on a pre-14 server. Java agents have had &lt;a href="/blog/posts/2019/11/26/small-aside-writing-agents-with-java-5-features"&gt;their own way of dealing with this for a while&lt;/a&gt;, though it seems like maybe they default to Java 8 now, which is good. With XPages, it seems like it's a little less obvious.&lt;/p&gt;
  588. &lt;p&gt;From my experience, this is what I've found:&lt;/p&gt;
  589. &lt;ul&gt;
  590. &lt;li&gt;Existing projects may have a specific compiler setting specified (similar to agents in the above-linked post) and so will remain as Java 1.8&lt;/li&gt;
  591. &lt;li&gt;New projects seem to use the active workspace setting, which will matter&lt;/li&gt;
  592. &lt;li&gt;Upgrades of Designer in place will keep the default compiler level for the workspace at 1.8&lt;/li&gt;
  593. &lt;li&gt;A fresh installation sets the compiler level to 17&lt;/li&gt;
  594. &lt;/ul&gt;
  595. &lt;p&gt;This is all... fine. It'd be nice if it was a little more obvious to the developer (like if it was controlled by the xsp.properties setting for minimum version), but this is more or less the Eclipse way of doing things. We'll just have to be aware for a while of these settings and their interactions when developing for pre-14 deployment. If you're targeting an older server, you should make sure that you go to Project Properties for your NSF and set the Java Compiler level to fit:&lt;/p&gt;
  596. &lt;img src="media/31344FB6E14D4DB688FDA745288E8205/nsf-compiler.png" alt="Screenshot of the Java Compiler settings for an NSF" title="nsf-compiler.png" border="0" width="674" height="214" /&gt;
  597. &lt;h2&gt;Less Critical&lt;/h2&gt;
  598. &lt;p&gt;Alright, that covers the big-ticket stuff, but there are a few more things to know.&lt;/p&gt;
  599. &lt;h4&gt;Java Deprecation Warning On HTTP Start&lt;/h4&gt;
  600. &lt;p&gt;This one is documented, though oddly &lt;a href="https://help.hcltechsw.com/domino/14.0.0/admin/wn_components_no_longer_included_in_release.html"&gt;in the &amp;quot;no longer included&amp;quot; page&lt;/a&gt;: when you start HTTP on Domino, you'll see a message like this:&lt;/p&gt;
  601. &lt;pre&gt;&lt;code&gt;WARNING: A terminally deprecated method in java.lang.System has been called
  602. WARNING: System::setSecurityManager has been called by lotus.notes.AgentSecurityManager (file:/C:/Domino/ndext/Notes.jar)
  603. WARNING: Please consider reporting this to the maintainers of lotus.notes.AgentSecurityManager
  604. WARNING: System::setSecurityManager will be removed in a future release
  605. &lt;/code&gt;&lt;/pre&gt;
  606. &lt;p&gt;This is actually totally fine. As it says, the whole SecurityManager apparatus is gone in future versions, but Notes and Domino still use it for agents (and, unfortunately, XPages). While I long for the day when I never have to think about it again, this is a reasonable-enough compromise for 14 as a &amp;quot;transitional&amp;quot; version in its Java journey. So... you can ignore this and not worry.&lt;/p&gt;
  607. &lt;h4&gt;&amp;quot;Java Main Sources&amp;quot; and &amp;quot;Java Test Sources&amp;quot; Working Sets&lt;/h4&gt;
  608. &lt;p&gt;If you use Working Sets in Designer, you may notice two entries not of your creation:&lt;/p&gt;
  609. &lt;img src="media/F5C1B15295674767A01C403156176B5E/designer-working-sets.png" alt="Screenshot of the &amp;#39;Select Working Set&amp;#39; dialog in Designer 14" title="designer-working-sets.png" border="0" width="453" height="129" /&gt;
  610. &lt;p&gt;These showed up in Eclipse somewhere along the line, presumably to make it easier for people to select those types of projects without manually managing their working sets. Designer inherits this and HCL didn't remove them, but you're free to delete them if you want.&lt;/p&gt;
  611. &lt;h4&gt;&amp;quot;AbstractCompiledPage cannot be resolved&amp;quot;&lt;/h4&gt;
  612. &lt;p&gt;This is another one that came up back in 9.0.1FP10 and it's still here, but it's less of an issue: every once in a while, when building a project, Designer will complain about the &lt;code&gt;AbstractCompiledPage&lt;/code&gt; class not being found. In my experience, this only shows up temporarily during a build, but it's worth noting that, if it &lt;em&gt;does&lt;/em&gt; stick around for you, a Project - Clean should fix it.&lt;/p&gt;
  613. &lt;h4&gt;JAX-B and CORBA&lt;/h4&gt;
  614. &lt;p&gt;After Java 8, a few Java EE components were removed from the normal JRE distribution in favor of developing them in Java EE, then Jakarta EE. JAX-B is one of them (now &lt;a href="https://jakarta.ee/specifications/xml-binding/"&gt;Jakarta XML Binding&lt;/a&gt;) - we don't normally use this directly, but it comes up sometimes, either directly (likely as one of the other old-timey BASE64 workarounds) or as a dependency. It shouldn't matter in Designer, but, if you're writing Domino-targeting code outside Designer, you may need to be aware. One way or another, you should add this as a dependency - in a basic case, you can add &amp;quot;jakarta.xml.bind-api.jar&amp;quot; and &amp;quot;jaxb-impl.jar&amp;quot; from &amp;quot;ndext&amp;quot; to your build path.&lt;/p&gt;
  615. &lt;p&gt;Similarly, CORBA support classes (&amp;quot;org.omg&amp;quot; stuff, usually) used to come with the JRE and now don't. To work around this, HCL did what I've done in the past and included &amp;quot;glassfish-corba-omgapi.jar&amp;quot;, in their case putting it in &amp;quot;ndext&amp;quot;. If you're using Notes.jar with Java &amp;gt; 8 outside Designer, you'll need this one too.&lt;/p&gt;
  616. &lt;h2&gt;Conclusion&lt;/h2&gt;
  617. &lt;p&gt;I think that covers it for now. Considering how much changed with Java from 8 to 17, this could have been a lot rougher, though I fear that some of these workarounds will plague us for a long time.&lt;/p&gt;
  618. &lt;p&gt;In the mean time, I'd appreciate it if you vote for &lt;a href="https://domino-ideas.hcltechsw.com/ideas/DOMINO-I-2594"&gt;this Aha idea&lt;/a&gt; to keep Domino on a good Java update cadence. It'd be a shame if we sit on 17 right up until the very end of its life, as we did with 6 and 8, in part since these multi-version moves are more painful than single-LTS updates.&lt;/p&gt;
  619. </description></item><item><title>XPages JEE 2.14.0</title><link>https://frostillic.us/blog/posts/2023/10/27/xpages-jee-2-14-0</link><content:encoded>&lt;p&gt;Today, I released &lt;a href="https://openntf.org/main.nsf/project.xsp?r=project/XPages%20Jakarta%20EE%20Support/releases/3DB9EC4815B7385186258A55005598B9"&gt;version 2.14.0 of the XPages Jakarta EE Support project&lt;/a&gt;. As with the last few releases, this is primarily about bug fixes and compatibility as I prepare for the big switch in 3.0, but there are some notable, if small, feature additions.&lt;/p&gt;
  620. &lt;p&gt;To begin with, I improved handling of reading JSON in NoSQL entities when reading from a view. This applies to the &lt;code&gt;@ItemStorage(type=ItemStorage.Type.JSON)&lt;/code&gt; annotation on entity properties, which causes the value to be loaded and stored as JSON, useful for storing custom class types in a document. Now, such values can be read from view entries - previously, this processing was skipped for those. Of note when using this: normally, storing as JSON will automatically set the item's summary flag to false, to avoid overflowing the summary limit. However, you can add &lt;code&gt;@ItemFlags(summary=true)&lt;/code&gt; to the property to override this behavior so that the values can show up in views.&lt;/p&gt;
  621. &lt;p&gt;Additionally, I added the ability to use &lt;code&gt;JAXRSClassContributor&lt;/code&gt;s inside the NSF. These were originally an internal mechanism for the project to dynamically add REST endpoints and extensions, like those used by MVC and OpenAPI. Now, though, I've made it so that such classes can be registered via a file named &amp;quot;META-INF/services/org.openntf.xsp.jaxrs.JAXRSClassContributor&amp;quot; in the NSF, and also added the ability to specify configuration properties. The latter is important because, though all xsp.properties values were already inserted into the JAX-RS configuration, there was no way to provide non-string values. This came up in the context of MVC, which has a CSRF configuration property that must be an enum value.&lt;/p&gt;
  622. &lt;p&gt;For the final feature, I improved support for JAX-RS's status-indicating exceptions, such as &lt;code&gt;NotSupportedException&lt;/code&gt; and &lt;code&gt;BadRequestException&lt;/code&gt;. Previously, the project supported translating &lt;code&gt;NotFoundException&lt;/code&gt; to a 404, but now it will also translate these other standard exceptions to their corresponding HTTP statuses.&lt;/p&gt;
  623. &lt;p&gt;The release is otherwise rounded out by a number of bug fixes to fix problems encountered in the wild. Additionally, I added a workaround for some classpath pollution in the latest Domino 14 beta - I hope that the trouble will be gone for GA, but this project should handle it either way.&lt;/p&gt;
  624. </content:encoded><guid>09CFFF4ED3DC48039EB3281AF7EB565D</guid><dc:creator></dc:creator><dc:date>2023-10-27T15:47:02Z</dc:date><pubDate>Fri, 27 Oct 2023 15:47:02 GMT</pubDate><description>&lt;p&gt;Today, I released &lt;a href="https://openntf.org/main.nsf/project.xsp?r=project/XPages%20Jakarta%20EE%20Support/releases/3DB9EC4815B7385186258A55005598B9"&gt;version 2.14.0 of the XPages Jakarta EE Support project&lt;/a&gt;. As with the last few releases, this is primarily about bug fixes and compatibility as I prepare for the big switch in 3.0, but there are some notable, if small, feature additions.&lt;/p&gt;
  625. &lt;p&gt;To begin with, I improved handling of reading JSON in NoSQL entities when reading from a view. This applies to the &lt;code&gt;@ItemStorage(type=ItemStorage.Type.JSON)&lt;/code&gt; annotation on entity properties, which causes the value to be loaded and stored as JSON, useful for storing custom class types in a document. Now, such values can be read from view entries - previously, this processing was skipped for those. Of note when using this: normally, storing as JSON will automatically set the item's summary flag to false, to avoid overflowing the summary limit. However, you can add &lt;code&gt;@ItemFlags(summary=true)&lt;/code&gt; to the property to override this behavior so that the values can show up in views.&lt;/p&gt;
  626. &lt;p&gt;Additionally, I added the ability to use &lt;code&gt;JAXRSClassContributor&lt;/code&gt;s inside the NSF. These were originally an internal mechanism for the project to dynamically add REST endpoints and extensions, like those used by MVC and OpenAPI. Now, though, I've made it so that such classes can be registered via a file named &amp;quot;META-INF/services/org.openntf.xsp.jaxrs.JAXRSClassContributor&amp;quot; in the NSF, and also added the ability to specify configuration properties. The latter is important because, though all xsp.properties values were already inserted into the JAX-RS configuration, there was no way to provide non-string values. This came up in the context of MVC, which has a CSRF configuration property that must be an enum value.&lt;/p&gt;
  627. &lt;p&gt;For the final feature, I improved support for JAX-RS's status-indicating exceptions, such as &lt;code&gt;NotSupportedException&lt;/code&gt; and &lt;code&gt;BadRequestException&lt;/code&gt;. Previously, the project supported translating &lt;code&gt;NotFoundException&lt;/code&gt; to a 404, but now it will also translate these other standard exceptions to their corresponding HTTP statuses.&lt;/p&gt;
  628. &lt;p&gt;The release is otherwise rounded out by a number of bug fixes to fix problems encountered in the wild. Additionally, I added a workaround for some classpath pollution in the latest Domino 14 beta - I hope that the trouble will be gone for GA, but this project should handle it either way.&lt;/p&gt;
  629. </description></item><item><title>Overdue CollabSphere Followup</title><link>https://frostillic.us/blog/posts/2023/10/11/overdue-collabsphere-followup</link><content:encoded>&lt;p&gt;Though it's been over a month since &lt;a href="https://collabsphere.org/ug/cs2023.nsf/index.html"&gt;CollabSphere 2023&lt;/a&gt;, I've somehow now yet gotten around to talking about it here. Time to remedy that!&lt;/p&gt;
  630. &lt;h3&gt;Webinar and Workshop&lt;/h3&gt;
  631. &lt;p&gt;I presented a couple sessions at CollabSphere, but the meatiest was my workshop on the &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee"&gt;XPages Jakarta EE project&lt;/a&gt;. A bit before the show, I wrote &lt;a href="/blog/posts/2023/7/28/modes-of-app-development-with-xpages-jakarta-ee"&gt;a post discussing modes of development with it&lt;/a&gt; and that formed the structure of the workshop.&lt;/p&gt;
  632. &lt;p&gt;It &lt;em&gt;also&lt;/em&gt; formed the structure of August's OpenNTF webinar. Fortunately, unlike the workshop, the webinar was recorded, and that is &lt;a href="https://youtu.be/yPeq9BPhGmE"&gt;up on OpenNTF's YouTube channel&lt;/a&gt;. Though the workshop was longer and had some refinements and audience discussion, they both worked from the same original slide deck and so the webinar did a pretty good job of covering the same material.&lt;/p&gt;
  633. &lt;p&gt;As part of the presentations, I created four versions of the same basic to-do tracker app, written in each of the four modes I talked about. I put them up &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/tree/a03dfba875e9b3ed4dd49bea0af4a536e51d226a/examples/todo"&gt;in the project repository&lt;/a&gt; with a quick README introducing each of them. With the project installed (they were written to 2.13.0 and above), you should be able to sync the ODPs with NSFs in Designer and poke around yourself. They're all intentionally-similarly structured and basic, with all of their elements in either Java classes, file resources, or stylesheets. They're also all intentionally under-developed, so you're not allowed to make fun of them.&lt;/p&gt;
  634. &lt;h3&gt;JNX&lt;/h3&gt;
  635. &lt;p&gt;This one will be a doozy! In fact, it's &lt;em&gt;such&lt;/em&gt; a doozy that I'm going to keep kicking the can down the road as far as properly talking about it.&lt;/p&gt;
  636. &lt;p&gt;In short, &lt;a href="https://github.com/HCL-TECH-SOFTWARE/domino-jnx"&gt;Domino JNX&lt;/a&gt; is a more-modern API for Domino access - it was initiated and is used heavily by the Domino REST API (DRAPI), but also stands on its own. It started out essentially as another swing at &lt;a href="https://github.com/klehmann/domino-jna/"&gt;Karsten Lehmann's Domino JNA&lt;/a&gt; and shares a lot of the same ideas and approaches (and code - the committer info in the GitHub repo is deceptive, as I got to paste a whole mountain of existing code from another repo into this one and thus got credit for it).&lt;/p&gt;
  637. &lt;p&gt;Now that it's open source, there's some significant work to do as far as documentation, examples, integration, and distribution go. I plan to find time to improve a lot of these aspects, including blogging here, though it's the sort of thing that always ends up below other priorities.&lt;/p&gt;
  638. &lt;p&gt;In any event, it's quite neat and has a lot of good capabilities. I plan to write a driver for JNoSQL for it, either to be alongside or to wholly supplant the Notes.jar-based one currently used by the project. The neat thing there is that users of XPages JEE won't have to care much: it'll just get a bit faster in parts but should largely work the same, except maybe with a change to the way you point at other databases.&lt;/p&gt;
  639. </content:encoded><guid>1B489C1F979D491CBCC10C4F9116A153</guid><dc:creator>Jesse Gallagher</dc:creator><dc:date>2023-10-11T20:59:29.579Z</dc:date><pubDate>Wed, 11 Oct 2023 20:59:29 GMT</pubDate><description>&lt;p&gt;Though it's been over a month since &lt;a href="https://collabsphere.org/ug/cs2023.nsf/index.html"&gt;CollabSphere 2023&lt;/a&gt;, I've somehow now yet gotten around to talking about it here. Time to remedy that!&lt;/p&gt;
  640. &lt;h3&gt;Webinar and Workshop&lt;/h3&gt;
  641. &lt;p&gt;I presented a couple sessions at CollabSphere, but the meatiest was my workshop on the &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee"&gt;XPages Jakarta EE project&lt;/a&gt;. A bit before the show, I wrote &lt;a href="/blog/posts/2023/7/28/modes-of-app-development-with-xpages-jakarta-ee"&gt;a post discussing modes of development with it&lt;/a&gt; and that formed the structure of the workshop.&lt;/p&gt;
  642. &lt;p&gt;It &lt;em&gt;also&lt;/em&gt; formed the structure of August's OpenNTF webinar. Fortunately, unlike the workshop, the webinar was recorded, and that is &lt;a href="https://youtu.be/yPeq9BPhGmE"&gt;up on OpenNTF's YouTube channel&lt;/a&gt;. Though the workshop was longer and had some refinements and audience discussion, they both worked from the same original slide deck and so the webinar did a pretty good job of covering the same material.&lt;/p&gt;
  643. &lt;p&gt;As part of the presentations, I created four versions of the same basic to-do tracker app, written in each of the four modes I talked about. I put them up &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/tree/a03dfba875e9b3ed4dd49bea0af4a536e51d226a/examples/todo"&gt;in the project repository&lt;/a&gt; with a quick README introducing each of them. With the project installed (they were written to 2.13.0 and above), you should be able to sync the ODPs with NSFs in Designer and poke around yourself. They're all intentionally-similarly structured and basic, with all of their elements in either Java classes, file resources, or stylesheets. They're also all intentionally under-developed, so you're not allowed to make fun of them.&lt;/p&gt;
  644. &lt;h3&gt;JNX&lt;/h3&gt;
  645. &lt;p&gt;This one will be a doozy! In fact, it's &lt;em&gt;such&lt;/em&gt; a doozy that I'm going to keep kicking the can down the road as far as properly talking about it.&lt;/p&gt;
  646. &lt;p&gt;In short, &lt;a href="https://github.com/HCL-TECH-SOFTWARE/domino-jnx"&gt;Domino JNX&lt;/a&gt; is a more-modern API for Domino access - it was initiated and is used heavily by the Domino REST API (DRAPI), but also stands on its own. It started out essentially as another swing at &lt;a href="https://github.com/klehmann/domino-jna/"&gt;Karsten Lehmann's Domino JNA&lt;/a&gt; and shares a lot of the same ideas and approaches (and code - the committer info in the GitHub repo is deceptive, as I got to paste a whole mountain of existing code from another repo into this one and thus got credit for it).&lt;/p&gt;
  647. &lt;p&gt;Now that it's open source, there's some significant work to do as far as documentation, examples, integration, and distribution go. I plan to find time to improve a lot of these aspects, including blogging here, though it's the sort of thing that always ends up below other priorities.&lt;/p&gt;
  648. &lt;p&gt;In any event, it's quite neat and has a lot of good capabilities. I plan to write a driver for JNoSQL for it, either to be alongside or to wholly supplant the Notes.jar-based one currently used by the project. The neat thing there is that users of XPages JEE won't have to care much: it'll just get a bit faster in parts but should largely work the same, except maybe with a change to the way you point at other databases.&lt;/p&gt;
  649. </description></item><item><title>New Tiny Project: Wink Chattiness Patch</title><link>https://frostillic.us/blog/posts/2023/9/18/new-tiny-project-wink-chattiness-patch</link><content:encoded>&lt;p&gt;I've been using the Domino 14 betas for development for a while now, and one of the things that has driven me a little nuts is the way Wink spews a bunch of INFO-level logs to the server console when the XPages runtime initializes. You've probably seen it - this stuff:&lt;/p&gt;
  650. &lt;img src="media/33B3CEEB97A84299B4DAEC98B9F65972/wink-logs.png" alt="Screenshot of a Domino console on Windows displaying Wink INFO logs from Verse" title="Screenshot of a Domino console on Windows displaying Wink INFO logs from Verse" border="0" width="1282" height="264" /&gt;
  651. &lt;p&gt;It goes on for a while like that.&lt;/p&gt;
  652. &lt;p&gt;This isn't new with 14 as such - it's just that 14 now ships with Verse by default, and Verse uses the Wink distribution that came along with the Extension Library, and so now &lt;em&gt;everyone&lt;/em&gt; sees this.&lt;/p&gt;
  653. &lt;p&gt;I had encountered this before, back when one of my client projects used Wink before transitioning to what became &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/"&gt;the XPages JEE project&lt;/a&gt;. Back then, I wrote a shim in the project's Activator to reflectively insert replacements for each logger object. After seeing these messages from Verse for the millionth time, I decided to dust that off and turn it into a little project.&lt;/p&gt;
  654. &lt;p&gt;Thus was born &lt;a href="https://github.com/jesse-gallagher/wink-chattiness-patch"&gt;the Wink Chattiness Patch&lt;/a&gt;, a project with an expected &lt;a href="https://github.com/jesse-gallagher/wink-chattiness-patch/releases/tag/1.0.0"&gt;single release&lt;/a&gt; that has a simple purpose: when installed, it makes Wink less annoying.&lt;/p&gt;
  655. &lt;p&gt;This version is actually a bit more clever than the original from my client project. Part of that was born out of necessity: originally, the shim involved writing to &lt;code&gt;final&lt;/code&gt; fields in classes, but that's no longer possible (normally) &lt;a href="http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-November/056486.html"&gt;since Java 12&lt;/a&gt;, so that path was out. Instead, now I pre-populate the internal &lt;code&gt;Logger&lt;/code&gt; cache with my shim objects. I also made them a bit better: rather than just lessening the threshold for logging from INFO to WARN, they redirect to java.util.logging, which then will log to error-log-*.xml as appropriate, like other parts of the XPages stack.&lt;/p&gt;
  656. &lt;p&gt;Ideally, HCL will improve this themselves (I recommend they look at replacing &lt;code&gt;slf4j-simple&lt;/code&gt; in the Wink bundle with &lt;a href="https://search.maven.org/artifact/org.slf4j/slf4j-jdk14/1.6.1/jar"&gt;&lt;code&gt;slf4j-jdk14&lt;/code&gt;&lt;/a&gt;, which is probably the most-expedient path), but, failing that, this patch should make your Domino console just a bit less hairy.&lt;/p&gt;
  657. </content:encoded><guid>C9A7EACBD4B342B48DF63A4DD6886608</guid><dc:creator>Jesse Gallagher</dc:creator><dc:date>2023-09-18T21:24:06.316Z</dc:date><pubDate>Mon, 18 Sep 2023 21:24:06 GMT</pubDate><description>&lt;p&gt;I've been using the Domino 14 betas for development for a while now, and one of the things that has driven me a little nuts is the way Wink spews a bunch of INFO-level logs to the server console when the XPages runtime initializes. You've probably seen it - this stuff:&lt;/p&gt;
  658. &lt;img src="media/33B3CEEB97A84299B4DAEC98B9F65972/wink-logs.png" alt="Screenshot of a Domino console on Windows displaying Wink INFO logs from Verse" title="Screenshot of a Domino console on Windows displaying Wink INFO logs from Verse" border="0" width="1282" height="264" /&gt;
  659. &lt;p&gt;It goes on for a while like that.&lt;/p&gt;
  660. &lt;p&gt;This isn't new with 14 as such - it's just that 14 now ships with Verse by default, and Verse uses the Wink distribution that came along with the Extension Library, and so now &lt;em&gt;everyone&lt;/em&gt; sees this.&lt;/p&gt;
  661. &lt;p&gt;I had encountered this before, back when one of my client projects used Wink before transitioning to what became &lt;a href="https://github.com/OpenNTF/org.openntf.xsp.jakartaee/"&gt;the XPages JEE project&lt;/a&gt;. Back then, I wrote a shim in the project's Activator to reflectively insert replacements for each logger object. After seeing these messages from Verse for the millionth time, I decided to dust that off and turn it into a little project.&lt;/p&gt;
  662. &lt;p&gt;Thus was born &lt;a href="https://github.com/jesse-gallagher/wink-chattiness-patch"&gt;the Wink Chattiness Patch&lt;/a&gt;, a project with an expected &lt;a href="https://github.com/jesse-gallagher/wink-chattiness-patch/releases/tag/1.0.0"&gt;single release&lt;/a&gt; that has a simple purpose: when installed, it makes Wink less annoying.&lt;/p&gt;
  663. &lt;p&gt;This version is actually a bit more clever than the original from my client project. Part of that was born out of necessity: originally, the shim involved writing to &lt;code&gt;final&lt;/code&gt; fields in classes, but that's no longer possible (normally) &lt;a href="http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-November/056486.html"&gt;since Java 12&lt;/a&gt;, so that path was out. Instead, now I pre-populate the internal &lt;code&gt;Logger&lt;/code&gt; cache with my shim objects. I also made them a bit better: rather than just lessening the threshold for logging from INFO to WARN, they redirect to java.util.logging, which then will log to error-log-*.xml as appropriate, like other parts of the XPages stack.&lt;/p&gt;
  664. &lt;p&gt;Ideally, HCL will improve this themselves (I recommend they look at replacing &lt;code&gt;slf4j-simple&lt;/code&gt; in the Wink bundle with &lt;a href="https://search.maven.org/artifact/org.slf4j/slf4j-jdk14/1.6.1/jar"&gt;&lt;code&gt;slf4j-jdk14&lt;/code&gt;&lt;/a&gt;, which is probably the most-expedient path), but, failing that, this patch should make your Domino console just a bit less hairy.&lt;/p&gt;
  665. </description></item><atom:link href="https://frostillic.us/blog/blog.xml" rel="self" type="application/rss+xml"/></channel></rss>
Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda