<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Shai Almog on Medium]]></title>
        <description><![CDATA[Stories by Shai Almog on Medium]]></description>
        <link>https://medium.com/@Codename_One?source=rss-a8cc878ccaaa------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*kGBjWsDV0sjwOdBTs2jnmQ.jpeg</url>
            <title>Stories by Shai Almog on Medium</title>
            <link>https://medium.com/@Codename_One?source=rss-a8cc878ccaaa------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 01:55:15 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@Codename_One/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[iOS 27 Glass You Can Choose, Measure, and Test]]></title>
            <link>https://medium.com/javarevisited/ios-27-glass-you-can-choose-measure-and-test-92611a4c388e?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/92611a4c388e</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Sat, 07 Nov 2026 09:49:48 GMT</pubDate>
            <atom:updated>2026-11-07T09:49:48.811Z</atom:updated>
            <content:encoded><![CDATA[<h4>Select the iOS 27 theme independently of Xcode, see the revised glass in motion, and explore local iPhone Duo posture support.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zFDiQjdwu7uY9pA6FhxZDw.jpeg" /></figure><p>A tab bar can look right in a screenshot and feel wrong the first time you tap it. Its selection moves, stretches, samples the content underneath, and settles. A still image catches one moment in that process.</p><p>Alongside that rendering work, we’re bringing back an option long-time Codename One users will remember: choosing the Xcode version on our build servers. It follows <a href="https://www.codenameone.com/blog/xcode-27-build-settings/">last week’s build migration</a>. We have separate iOS 26 and 27 theme generations, revised glass materials, and a closer look at the tab-selection animation. We also have local iPhone Duo support, with a different validation boundary from the ordinary iOS 27 CI runs.</p><h3>Xcode Selection Is Back</h3><p>You can ask the cloud build service to use Xcode 27 now. In the Settings app, under <strong>Build Hints</strong>, add:</p><pre>ios.xcode_version=27</pre><p>If you’ve been using Codename One for a while, this might look familiar. We supported Xcode selection in the past, then discontinued it because Apple’s Xcode upgrades kept forcing us to upgrade macOS too. With Xcode 27, that OS upgrade isn’t required yet. We can use virtualization to make both versions available again.</p><p><strong>The current default is Xcode 26. We recommend leaving </strong><strong>ios.xcode_version unset for most apps.</strong> The hint is mainly for testing against a newer toolchain, or for using a specific feature that needs the new SDK. There is no need to set it just to keep your app up to date with the build service.</p><p>Xcode 27.1 is still a beta. We’ll add it when it’s released, and we’ll switch the default to Xcode 27 when Apple’s requirements call for it. Leaving the hint unset lets your builds follow that default.</p><h3>Choose the Theme Separately</h3><p>Set these values in the Settings app under <strong>Build Hints</strong>:</p><pre>ios.themeMode=modern<br>ios.themeGeneration=27</pre><p>If you want native themes across platforms, use the shared hint instead of ios.themeMode:</p><pre>nativeTheme=native<br>ios.themeGeneration=27</pre><p>On iOS, that selects the modern family. It also enables the desktop platform themes, which nativeTheme=modern deliberately leaves unchanged. An explicit ios.themeMode takes precedence over the shared setting. See the <a href="https://github.com/codenameone/CodenameOne/blob/master/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java">theme selection in the iOS builder</a>.</p><p>Setting What it chooses What leaving it unset means ios.themeMode The iOS theme family, overriding the shared hint Follows nativeTheme; with neither set, keeps the existing iOS 7 theme ios.themeGeneration Generation 26 or 27 of the modern family Generation 26 ios.xcode_version A toolchain on the cloud build service Xcode 26, the current service default</p><p>The generation hint is consulted only for the modern theme. An older theme doesn’t turn modern because you set 27 beside it.</p><p>An explicit Xcode version must exist on the server handling the cloud build. An unavailable version fails instead of silently substituting another SDK. This is a server selection hint; it doesn’t install Xcode or change a local build. The <a href="https://github.com/codenameone/CodenameOne/blob/master/CodenameOne/src/com/codename1/annotations/buildhints/Ios.java">hint definition</a> documents that distinction.</p><h3>Local Builds Use Your Xcode Installation</h3><p>For a local build on a Mac with Xcode 27 installed, select that installation for the build process. For example, with the application at /Applications/Xcode27.app:</p><pre>DEVELOPER_DIR=/Applications/Xcode27.app/Contents/Developer \<br>  mvn cn1:buildIosXcodeProject</pre><p>Use the same installation when building and running the generated project. The <a href="https://github.com/codenameone/CodenameOne/blob/master/.github/workflows/scripts-ios.yml">CI screenshot workflow</a> and <a href="https://github.com/codenameone/CodenameOne/blob/master/.github/workflows/scripts-fidelity.yml">fidelity workflow</a> now have Xcode 27 legs alongside the existing generation. A workflow entry proves what is configured; the result of a particular run still matters.</p><h3>Dark glass needs an edge</h3><p>A blur softens the backdrop. It doesn’t by itself make a dark floating surface distinguishable from the dark content behind it.</p><p>The iOS 27 rendering work adjusts the luminance curve and adds the dark outline visible around the native floating material. We group the material settings into recipes: chrome for edge bars, pill for the floating tab surface, and panel for panels and buttons. A UIID selects the appropriate recipe instead of independently tuning a pile of glass constants.</p><p>The theme sources are split into <a href="https://github.com/codenameone/CodenameOne/tree/master/native-themes/ios-modern">common rules and generation-specific rules</a>. That keeps ordinary controls shared while allowing the glass treatment to change. It also gives your own CSS the same place to override the result.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/717/1*7M-BdfPGM4Nip2SkqbS9dg.png" /></figure><h3>See the Glass in Motion</h3><p>The selection stretches as it moves between tabs. The backdrop and edge treatment have to remain readable throughout that movement, including against dark content:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*UixsRAkiv9XOPnXN-fwbpQ.gif" /></figure><p><em>Codename One’s iOS renderer on an iPhone 16 simulator running iOS 27.0. These are deterministic samples across the theme’s 480 ms animation, with holds before and after. The clip shows the rendered motion; it isn’t a live frame-rate measurement.</em></p><p>We use fixed samples to catch rendering regressions while changing the glass recipes. There is still work to do on fidelity, and a short animation can’t tell you how the theme will behave over all of your app’s content. Try it over images, scrolling lists, and your own brand colors before adopting it.</p><p><a href="https://github.com/codenameone/CodenameOne/pull/5888">PR #5888</a> refines the dark rendering and animation tests; <a href="https://github.com/codenameone/CodenameOne/pull/5876">PR #5876</a> connects the selected generation through to the device theme.</p><h3>A closed foldable still has a hinge</h3><p>The Duo work exposed a useful trap. A closed device can report no dividing screen region while still reporting a live, closed hinge. If you identify foldables only by looking for a region that divides the screen, the device stops being a foldable precisely when it folds shut.</p><p>The iOS bridge uses hinge state as well as division regions. It also distinguishes a fold from an occlusion such as the camera housing. The public Java API stays DevicePosture:</p><pre>import com.codename1.ui.CN;<br>import com.codename1.ui.DevicePosture;<br> <br>DevicePosture posture = CN.getDevicePosture();<br>if (posture.isFoldable()) {<br>    int angle = posture.getHingeAngle(); // Degrees; -1 means unknown.<br>    int state = posture.getPosture();<br>    // Choose the layout for this state, then observe later posture changes.<br>}</pre><p>For a top-and-bottom layout, also check that getFoldOrientation() is FOLD_ORIENTATION_HORIZONTAL. A partially open book posture needs a different layout. Keep important controls outside getFoldBounds(null) when a separating region exists.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hyfCrroKUC6QFfxs4RtoWQ.jpeg" /></figure><p><em>Local capture of the installed HelloCodenameOne instrumentation app in the Duo simulator, including the simulator device frame. This shows the app running on the beta device profile. It is not a fold-transition test or a demonstration of the new glass theme.</em></p><p><strong>Duo support has been tested locally with the iOS 27.1 beta simulator. It has not been validated on shipping Duo hardware.</strong> Hinge support requires the iOS 27.1 SDK at compile time as well as a supporting runtime. The bridge checks for UIKit/UIHingeInteraction.h before compiling the implementation, then checks runtime availability before calling it. The cloud&#39;s Xcode 27.0 toolchain does not include that header, so ios.xcode_version=27 builds contain no hinge implementation, even if installed on iOS 27.1. For now, testing the hinge API requires a local build with the Xcode 27.1 beta. We expect the path to carry forward when the SDK is released, but that still needs a release-SDK and device check. The <a href="https://github.com/codenameone/CodenameOne/blob/master/Ports/iOSPort/nativeSources/CN1Hinge.m">bridge source</a> and <a href="https://github.com/codenameone/CodenameOne/pull/5871">PR #5871</a> show the boundary.</p><h3>Health data needs the same precision</h3><p>The Health API adds HealthDataType.HEART_RATE_VARIABILITY_RMSSD. RMSSD and SDNN are different heart-rate variability statistics. The API doesn&#39;t substitute one for the other when a platform lacks the requested type.</p><p>RMSSD on iOS requires both the iOS 27 SDK at build time and a supporting runtime. An older combination reports TYPE_NOT_SUPPORTED. Android Health Connect has RMSSD, while SDNN is unsupported there. That is API interoperability, not an assertion that two different measurements can be compared as if they were the same. The <a href="https://www.codenameone.com/developer-guide/health/">Health guide</a> explains the mapping.</p><h3>One iOS renderer to maintain</h3><p>We removed the OpenGL ES renderer in <a href="https://github.com/codenameone/CodenameOne/pull/5875">PR #5875</a>. Metal now handles iOS rendering. Remove ios.metal from old projects; the hint that previously selected the rendering path is no longer valid.</p><p>That removes a deprecated path from the compiler output and from our maintenance work. We aren’t attaching an unmeasured build-speed number to it. The practical benefit is that the renderer receiving these glass fixes is also the renderer our iOS builds use.</p><p>Start with a copy of a screen you know well. Build against the selected Xcode installation, enable generation 27, and check light and dark appearances while interacting with it. Then test your branded overrides and any embedded native peers. The <a href="https://www.codenameone.com/blog/who-decides-your-app-redesign/">weekly overview</a> explains the wider release; tomorrow we move from UI state to managed database state.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=92611a4c388e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javarevisited/ios-27-glass-you-can-choose-measure-and-test-92611a4c388e">iOS 27 Glass You Can Choose, Measure, and Test</a> was originally published in <a href="https://medium.com/javarevisited">Javarevisited</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apple Shouldn’t Dictate Your App’s Redesign Schedule]]></title>
            <link>https://medium.com/javarevisited/apple-shouldnt-dictate-your-app-s-redesign-schedule-90aa9d5c3292?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/90aa9d5c3292</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Sat, 07 Nov 2026 09:49:33 GMT</pubDate>
            <atom:updated>2026-11-07T09:49:33.766Z</atom:updated>
            <content:encoded><![CDATA[<h4>Adopt Xcode 27 without moving your redesign schedule. This week: Liquid Glass, JPA inspired ORM, OpenTelemetry, WebSockets, push callbacks, and native desktop themes.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*S3YHu0vEyX-_G6mLUZMOHQ.jpeg" /></figure><p>Apple’s design team doesn’t know what’s on your backlog. A new iOS appearance might be exactly what you want for your next release. It might also arrive halfway through a customer rollout, when changing the navigation is the last thing you need.</p><p>We want to support the new SDK without making that decision for you. This week, you can select Xcode 27 on our build servers with the returning ios.xcode_version hint. Local builds and CI also support Xcode 27, and our iOS 27 theme gets more detailed Liquid Glass effects. You choose when to adopt that appearance. Codename One renders most controls itself, so the theme ships with your app. Updating the SDK can leave that theme and your application CSS in place.</p><p><a href="https://www.codenameone.com/blog/xcode-27-build-settings/">Last week</a> we covered the build settings needed for Xcode 27. This week we’ll show the glass in motion, explain the theme switches, and take a first look at the foldable Duo in the simulator. There is substantial work beyond iOS, too. The JPA inspired ORM is probably the biggest addition: relationships and managed sessions now work in both the app and the backend. OpenTelemetry and WebSocket support give that backend more of what a real service needs.</p><h3>Coming This Week</h3><p>We’ll cover the release in six follow-ups, with code you can try and screenshots of the UI changes:</p><ul><li><strong>Saturday, September 26:</strong> . Try Xcode 27 with the returning build hint, choose your theme independently, and see the glass animation and Duo simulator work.</li><li><strong>Sunday, September 27:</strong> . Model relationships, update managed objects, fetch the data a screen needs, and handle conflicting writes.</li><li><strong>Monday, September 28:</strong> . Connect app and backend spans with OpenTelemetry and send them to your existing tracing infrastructure.</li><li><strong>Tuesday, September 29:</strong> . Add persistent, two-way connections to your app. Our device-test screenshot receiver shows the server handling real traffic.</li><li><strong>Wednesday, September 30:</strong> . Receive signed delivery digests and remove invalid targets without deleting devices after temporary failures.</li><li><strong>Thursday, October 1:</strong> . Use Fluent, Aqua, or Adwaita with desktop dialogs and title bars. See them in our Settings app, then apply them to yours.</li></ul><h3>Xcode Selection Is Back</h3><p>Long-time Codename One users might recognize this hint. In the Settings app, under <strong>Build Hints</strong>, you can now select Xcode 27 on the cloud build service:</p><pre>ios.xcode_version=27</pre><p>We discontinued Xcode selection because Apple’s upgrades kept requiring a macOS upgrade too. That isn’t required for 27 yet, and virtualization lets us offer the choice again. <strong>Xcode 26 remains the default. Leave the hint unset unless you’re testing or need a new SDK feature.</strong> We’ll add 27.1 after beta and move the default to 27 according to Apple’s requirements.</p><p>This hint only affects cloud builds. Local builds use the Xcode installation selected on your Mac. The theme is a separate choice: ios.themeMode=modern or the shared nativeTheme=native selects the modern family, and ios.themeGeneration=27 selects its iOS 27 appearance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*UixsRAkiv9XOPnXN-fwbpQ.gif" /></figure><p><em>Deterministic samples of Codename One’s 480 ms tab-selection animation on an iPhone 16 simulator running iOS 27.0. The animation shows rendered motion, not a live frame-rate measurement.</em></p><p>Saturday’s follow-up covers the build and theme settings, refined glass materials, Health API changes, and local Duo testing. . Duo hinge support needs a local Xcode 27.1 beta build; the cloud’s Xcode 27.0 SDK doesn’t contain the hinge API.</p><p>We also removed OpenGL ES. Metal is now the iOS renderer, and old projects should remove the obsolete ios.metal hint. Dropping that deprecated path removes compiler warnings and lets us concentrate rendering work on Metal. See <a href="https://github.com/codenameone/CodenameOne/pull/5875">PR #5875</a>.</p><h3>JPA Inspired ORM</h3><p><a href="https://www.codenameone.com/blog/java-backend-shared-models/">Last week’s backend tutorial</a> used explicit foreign-key fields and immediate DAO writes. The new managed ORM adds relationships and a session that tracks the objects involved in one unit of work. The same mapping annotations work with client SQLite and the backend databases.</p><pre>Session session = entities.openSession();<br>try {<br>    session.beginTransaction();<br>    Customer customer = session.find(Customer.class, customerId);<br>    if (customer != null) {<br>        customer.name = &quot;Updated name&quot;;<br>    }<br>    session.commitTransaction();<br>} finally {<br>    session.close();<br>}</pre><p>Here entities is an EntityManager, Customer is a mapped entity, and customerId identifies the row. The session detects the changed field and writes it at flush time. Closing the session never commits unfinished work.</p><p>Relationships, lazy loading, version checks, and a supported JPQL subset make this a much larger change than another query convenience method. They also introduce choices a tutorial needs to explain: which side owns a relationship, when a query loads it, and what happens after the session closes. Sunday’s article works through those choices. This is a JPA-inspired API, with its own packages and documented limits, rather than a claim of complete JPA compatibility. See <a href="https://github.com/codenameone/CodenameOne/pull/5885">PR #5885</a>.</p><h3>OpenTelemetry Support</h3><p>A slow screen can be waiting for the network, the service, or one database query. A server log alone can’t show where the user spent that time.</p><p>Our new OpenTelemetry integration connects the app’s network requests to backend request and database spans. A span records one operation; related spans form a trace. An annotation enables the instrumentation, and the spans use the standard OTLP transport to reach a collector.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/769/1*08jnL24WBF5DNKfFQJmsFA.png" /></figure><p>That is useful for a team introducing a small service into a large organization. The new service can participate in the tracing system the operations team already uses. It doesn’t need its own isolated diagnostic UI. Monday’s article covers collector configuration, sampling, the app relay, and the exact instrumentation boundary in <a href="https://github.com/codenameone/CodenameOne/pull/5887">PR #5887</a>.</p><h3>WebSocket Support</h3><p>Your backend can now keep a connection open and send data when it changes. Live status screens, chat, and progress updates can use WebSockets instead of repeatedly polling an HTTP endpoint. Register a Java endpoint with @WebSocketMapping, then handle text and binary messages through the backend API.</p><p>We use the same implementation to receive screenshots from our device tests. Metadata arrives as text, the image arrives as binary, and the server acknowledges the saved image before the device continues. That gives the public API a regular workload in our CI: a lost message can interrupt a test run we depend on.</p><p>Tuesday’s article includes a complete endpoint and explains connection limits, timeouts, and how slow clients affect sending. It also follows the screenshot receiver from <a href="https://github.com/codenameone/CodenameOne/pull/5880">PR #5880</a>, including the legacy Windows test path that still uses the earlier server.</p><h3>Push Delivery Callbacks</h3><p>The cloud accepting a push request doesn’t mean the provider accepted every target. Delivery feedback gives your backend the later results in a signed daily digest. It is an organization-level setting on Pro and above.</p><p>The decision worth automating is narrow: remove a key after INVALID_TARGET. A temporary provider failure can exhaust its retry budget while the key remains valid. Wednesday&#39;s article shows how to verify the digest, deduplicate on deliveryId, and acknowledge only after database writes commit. The examples in <a href="https://github.com/codenameone/CodenameOne/pull/5890">PR #5890</a> cover the CN1 backend, Spring, MicroProfile, and Node/serverless JavaScript.</p><h3>Native Desktop Themes for Production Apps</h3><p>Last week’s <a href="https://www.codenameone.com/blog/native-desktop-themes-experiment/">native desktop themes</a> were an experiment. This release brings them into production use, with refined controls, light and dark appearances, and desktop window behavior integrated into the themes. Your app can select Fluent on Windows, Aqua on macOS, or Adwaita on Linux while keeping its own branding.</p><p>We’ve adopted them in the Codename One Settings tool. Its forms and dialogs helped us find and fix the details that isolated component screenshots missed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_8qYgk8LVouWkR-kp69B0Q.png" /></figure><p><em>The Settings tool with the Aqua theme, captured on macOS. Thursday’s article includes all three theme families in light and dark appearances.</em></p><p>The change reaches the window as well as the controls. Desktop native themes open ordinary dialogs in real OS windows. Packaged JavaSE apps use native title bars with all three themes by default; the simulator can instead use the title-bar style declared by the theme. Existing projects still choose whether to adopt these themes. <a href="https://github.com/codenameone/CodenameOne/pull/5886">PR #5886</a> shows the application work that exposed and helped refine the defaults.</p><h3>Shorten the trip from documentation to a running device</h3><p>Two smaller changes make the rest easier to investigate.</p><p>The generated agent skill now explains the native-device debugger and MCP workflows. An agent can inspect and operate the running app through semantic UI tools, while a debugger handles breakpoints and stack inspection. For an MCP-only iOS session, ios.onDeviceDebug.waitForAttach must be false: otherwise the application waits before start() and never opens its MCP listener. Physical iPhones also need a USB relay for that loopback port. The <a href="https://github.com/codenameone/CodenameOne/blob/master/scripts/initializr/common/src/main/resources/skill/references/on-device-debugging.md">on-device reference</a> includes the commands and platform limits from <a href="https://github.com/codenameone/CodenameOne/pull/5864">PR #5864</a>.</p><p>The <a href="https://www.codenameone.com/developer-guide/">Developer Guide</a> now has chapter pages with site search, navigation, and links to individual sections. The <a href="https://www.codenameone.com/developer-guide/single-page/">single-page edition</a> is still available. Following last week’s Javadoc work, guide links can take you to the relevant chapter instead of dropping you into the whole book. Both views come from the same AsciiDoc sources. <a href="https://github.com/codenameone/CodenameOne/pull/5860">PR #5860</a> contains the generator and compatibility routing.</p><h3>Put the Next Release on Your Schedule</h3><p>Supporting Xcode 27 is our job. Deciding when your app should look different belongs to you. The theme settings let you keep those decisions separate, whether you’re trying Liquid Glass on iOS or adopting a desktop theme for an app that started on a phone.</p><p>There’s plenty to use here even if you’re leaving the UI alone. A screen that edits related records can use a managed ORM session. A status screen can receive WebSocket updates. When a request is slow, OpenTelemetry can show which service or query needs attention. Those are changes you can put to work on the features already in your backlog.</p><p>Tomorrow we’ll start with the iOS changes, including the code to select a theme and the Duo simulator capture. Then we’ll spend the rest of the week working through the data, service, and desktop features in detail.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=90aa9d5c3292" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javarevisited/apple-shouldnt-dictate-your-app-s-redesign-schedule-90aa9d5c3292">Apple Shouldn’t Dictate Your App’s Redesign Schedule</a> was originally published in <a href="https://medium.com/javarevisited">Javarevisited</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[OpenTelemetry Support from App to Database]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/javarevisited/opentelemetry-support-from-app-to-database-41c4edc0cce3?source=rss-a8cc878ccaaa------2"><img src="https://cdn-images-1.medium.com/max/1024/1*iJ1syUAiLukp77_Ez-tIRQ.jpeg" width="1024"></a></p><p class="medium-feed-snippet">Enable distributed tracing in the app and native Java backend, propagate request context, and export database and HTTP spans through&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/javarevisited/opentelemetry-support-from-app-to-database-41c4edc0cce3?source=rss-a8cc878ccaaa------2">Continue reading on Javarevisited »</a></p></div>]]></description>
            <link>https://medium.com/javarevisited/opentelemetry-support-from-app-to-database-41c4edc0cce3?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/41c4edc0cce3</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Thu, 05 Nov 2026 14:58:06 GMT</pubDate>
            <atom:updated>2026-11-05T14:58:06.783Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[JPA Inspired ORM from SQLite to PostgreSQL]]></title>
            <link>https://medium.com/javarevisited/jpa-inspired-orm-from-sqlite-to-postgresql-64969a8c089f?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/64969a8c089f</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Thu, 05 Nov 2026 14:57:07 GMT</pubDate>
            <atom:updated>2026-11-05T14:57:07.768Z</atom:updated>
            <content:encoded><![CDATA[<h4>Use managed ORM sessions, relationships, lazy loading, JPQL queries, and optimistic locking with shared Java entities on the client and backend.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JRtWNxlJXDI8ZKlHe9kJHg.jpeg" /></figure><p>Changing a customer’s name should not require remembering which DAO to call after every assignment. Loading twenty orders should not accidentally fetch each customer’s entire history. Those are the kinds of problems that appear once an application grows beyond a few independent tables.</p><p><a href="https://www.codenameone.com/blog/java-backend-shared-models/">Last week’s ORM example</a> introduced shared entities and generated DAOs. This release adds a managed session: an object that keeps track of the entities involved in one unit of work, detects their changes, and coordinates persistence. Relationships now belong in the model too.</p><p>The client and backend use the same mapping annotations. The client stores data in SQLite; the backend supports SQLite, PostgreSQL, MySQL, and MariaDB. This is a JPA-inspired layer, not a JPA provider or a way to run arbitrary Hibernate code unchanged.</p><h3>Model a customer and their orders</h3><p>These are two separate source files in the same package. Existing Entity and Id annotations stay in com.codename1.annotations; the new relationship and version annotations live in com.codename1.annotations.db.</p><pre>// Customer.java<br>package com.example.model;<br> <br>import com.codename1.annotations.Entity;<br>import com.codename1.annotations.Id;<br>import com.codename1.annotations.db.OneToMany;<br>import com.codename1.annotations.db.Version;<br>import java.util.ArrayList;<br>import java.util.List;<br> <br>@Entity(table = &quot;customers&quot;)<br>public class Customer {<br>    @Id public long id;<br>    @Version public long version;<br>    public String name;<br> <br>    @OneToMany(mappedBy = &quot;customer&quot;)<br>    public List&lt;Order&gt; orders = new ArrayList&lt;Order&gt;();<br>}</pre><pre>// Order.java<br>package com.example.model;<br> <br>import com.codename1.annotations.Entity;<br>import com.codename1.annotations.Id;<br>import com.codename1.annotations.db.JoinColumn;<br>import com.codename1.annotations.db.ManyToOne;<br> <br>@Entity(table = &quot;purchase_orders&quot;)<br>public class Order {<br>    @Id public long id;<br> <br>    @ManyToOne<br>    @JoinColumn(name = &quot;customer_id&quot;)<br>    public Customer customer;<br> <br>    public long totalCents;<br>}</pre><p>Order.customer owns the foreign key. Customer.orders describes the other direction through mappedBy. Application code maintains both sides when it changes the in-memory relationship:</p><pre>order.customer = customer;<br>customer.orders.add(order);</pre><p>Those two assignments describe one relationship from two viewpoints. They don’t persist either object on their own. This mapping doesn’t request cascading, so the transaction below explicitly persists both.</p><h3>Commit the unit of work</h3><p>On the client, import com.codename1.orm.EntityManager. On the backend, use com.codename1.backend.orm.EntityManager. Both expose openSession() and the shared com.codename1.orm.session.Session interface.</p><p>For a development database, call session.createTables() before beginning a transaction to create missing mapped tables. Production schema changes belong in your migration process.</p><p>The following code takes an entity manager named entities, with the schema already created:</p><pre>import com.codename1.orm.session.Session;<br> <br>Session session = entities.openSession();<br>try {<br>    session.beginTransaction();<br> <br>    Customer customer = new Customer();<br>    customer.name = &quot;Alice&quot;;<br>    session.persist(customer);<br> <br>    Order order = new Order();<br>    order.customer = customer;<br>    order.totalCents = 4900;<br>    customer.orders.add(order);<br>    session.persist(order);<br> <br>    session.commitTransaction();<br>} finally {<br>    session.close();<br>}</pre><p>The build generates metadata and enhances entity access. The session uses that metadata to manage the object graph without runtime reflection or proxy generation. Generated IDs are filled in as inserts occur.</p><p>Closing a session rolls back an active transaction. It never commits. That makes the finally block meaningful when an insert or commit fails. A session belongs to one thread and one unit of work; don&#39;t put it in a shared backend controller field. A pooled backend entity manager can be shared while each request opens its own session.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/519/1*7Xhmar1DpMKpx7osaJJQeg.png" /></figure><p>The session keeps one object instance per entity identity. Finding the same row twice within that session gives application code one managed object to work with. Updating customer.name changes that object; a flush detects the difference and issues the update. Queries inside a transaction flush pending changes too, so a query can trigger database writes before your explicit commit.</p><h3>Fetch the screen you intend to show</h3><p>A relationship is also a loading decision. To-one mappings default to eager loading; to-many mappings default to lazy loading. You can override either default.</p><p>This query selects at most twenty customers and loads their otherwise-lazy order collections before returning:</p><pre>List&lt;Customer&gt; customers = session.query(Customer.class)<br>        .eq(&quot;name&quot;, &quot;Alice&quot;)<br>        .orderBy(&quot;id&quot;, true)<br>        .limit(20)<br>        .fetch(&quot;orders&quot;)<br>        .list();</pre><p>Without .fetch(&quot;orders&quot;), these collections remain unloaded until accessed inside the session. The eager Order.customer mapping would not demonstrate that difference.</p><p>The query uses Java field paths. The ORM resolves them into the SQL joins and parameters for the database in use. Pagination applies to the root entities before fetched collections are loaded.</p><p>A direct to-one fetch with a single-column key can be batched. So can an inverse foreign-key collection fetch in that case. Ordered, map, composite-key, and join-table collections currently use individual loaders. An ORM doesn’t make query count irrelevant; inspect the access pattern for the mapping you actually use.</p><p>To load a relationship deliberately before leaving the session:</p><pre>Customer customer = session.find(Customer.class, customerId);<br>if (customer != null &amp;&amp; !session.isLoaded(customer, &quot;orders&quot;)) {<br>    session.initialize(customer, &quot;orders&quot;);<br>}</pre><p>An unloaded relationship read after detachment or session close throws LazyInitializationException. Already-loaded data remains readable. Generated @Mapped serialization also refuses an unloaded association instead of quietly fetching it during output. Load what the response needs, or map to a smaller response object while the session is open.</p><p>On a phone, do this work off the event dispatch thread (EDT). Fetching is synchronous. The UI should receive ready data, not discover a database trip while painting or handling a tap.</p><h3>Query in the vocabulary of the model</h3><p>The supported JPQL subset gives more complex queries a familiar form:</p><pre>List&lt;Order&gt; orders = session.createQuery(<br>        &quot;select o from Order o &quot;<br>        + &quot;where o.customer.name = :name order by o.id&quot;,<br>        Order.class)<br>        .setParameter(&quot;name&quot;, &quot;Alice&quot;)<br>        .list();</pre><p>It includes relationship joins, named parameters, scalar projections, aggregates, grouping and HAVING, plus supported nested queries and bulk mutations. Unsupported syntax is rejected. It is deliberately described as a subset; a query accepted by another persistence provider still needs to fit this implementation.</p><p>Bound parameters carry values into SQL. They don’t make a field name supplied by an untrusted caller safe to use as a query structure. Keep the query shape under application control.</p><p>Bulk update and delete operations need a transaction. They flush pending changes, bypass per-entity callbacks and cascades, then clear the persistence context so later reads don’t reuse stale objects. Use them when you intend those semantics, not as a shorter spelling of a per-entity operation.</p><h3>Detect the edit that happened elsewhere</h3><p>Customer.version is an optimistic-lock field. An update checks the previous version, and a conflicting update or deletion produces OptimisticLockException.</p><p>Imagine two administrators opening the same customer. Both start with version 4. The first saves an edit and advances the version. The second cannot quietly overwrite that newer row with a change based on version 4.</p><p>The application still decides what the user should do next: reload, compare changes, or retry an operation that is safe to repeat. After an optimistic-lock or database failure, roll back an active transaction before continuing. isTransactionActive() lets you check whether the underlying transaction is still open.</p><p>A detached object has another important rule: session.merge(detached) returns the managed copy. Keep that return value. Calling merge doesn&#39;t attach the object you passed in.</p><h3>How much of JPA does this bring?</h3><p>The new layer includes all four relationship kinds, cascades, orphan removal, composite assigned IDs, generated ID strategies, embedded values, converters, lifecycle callbacks, and supported inheritance mappings. That is a substantial step beyond scalar DAOs.</p><p>The old scalar DAO API still writes immediately. Models using managed features require a session; asking the legacy DAO to handle them fails rather than silently losing relationship or version behavior. Production schema migration remains a deployment responsibility. Shared entity definitions also do not synchronize a phone database with a server database or resolve offline conflicts for you.</p><p>Start with one relationship and one explicit transaction. Check what gets loaded before adding more cascading or fetch behavior. The <a href="https://www.codenameone.com/developer-guide/annotation-sqlite-orm/">ORM guide</a> lists the supported mappings and query restrictions, and <a href="https://github.com/codenameone/CodenameOne/pull/5885">PR #5885</a> contains the implementation. Tomorrow’s explains how to see the backend’s database work inside a request trace.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=64969a8c089f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javarevisited/jpa-inspired-orm-from-sqlite-to-postgresql-64969a8c089f">JPA Inspired ORM from SQLite to PostgreSQL</a> was originally published in <a href="https://medium.com/javarevisited">Javarevisited</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Your Push Request Succeeded. The Device Key Is Dead.]]></title>
            <link>https://medium.com/javarevisited/your-push-request-succeeded-the-device-key-is-dead-e068d407279d?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/e068d407279d</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 15:25:04 GMT</pubDate>
            <atom:updated>2026-11-04T15:25:04.877Z</atom:updated>
            <content:encoded><![CDATA[<h4>Use signed BuildCloud delivery digests to retire invalid push targets, retain recoverable ones, and acknowledge outcomes after durable database updates.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4Spj33sWN2PpwPvRjbzSSQ.jpeg" /></figure><p>Your server sends a push request and gets a successful response. Somewhere in the target list is a device key that stopped working weeks ago. The response can’t identify it because the provider hasn’t answered yet.</p><p>BuildCloud now sends delivery feedback to your backend. It closes the gap between admitting the request and learning what happened during provider delivery, so your own device database can stop retaining keys that the provider has rejected.</p><p>The feedback is a <strong>signed daily digest</strong>, configured per organization on Pro and above. It isn’t an immediate webhook for every notification, and provider acceptance isn’t proof that a device displayed or a person read the message.</p><h3>Configure the return path</h3><p>In the Console, open <strong>Push &gt; Settings &gt; Delivery feedback</strong>. Set an HTTPS endpoint, enable the daily digest, and use <strong>Send test event</strong> to exercise the receiver. The card supplies the signing secret and reports what the endpoint returned.</p><p>The setting belongs to the organization, so a digest can contain outcomes for several applications. Store the deliveryId returned by a v3 send alongside the application and target you sent to. Classic sends don&#39;t return that ID, so their correlation uses the device key.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/276/1*y70bKBjmy8sBwHTOAToC1Q.png" /></figure><p>A successful send response is an admission result. The later digest describes provider outcomes. Keeping those two stages separate prevents a successful HTTP request from becoming a misleading “delivered” label in your own administration UI.</p><h3>Delete on the reason, not the status</h3><p>The digest itemizes failures and summarizes accepted deliveries. This shortened example shows the part a cleanup handler needs:</p><pre>{<br>  &quot;deliveryId&quot;: &quot;6f0f6f1e-9a1e-4f2a-9a7a-6d5f5f6a1b02&quot;,<br>  &quot;provider&quot;: &quot;fcm&quot;,<br>  &quot;token&quot;: &quot;example-device-token&quot;,<br>  &quot;status&quot;: &quot;FAILED&quot;,<br>  &quot;reason&quot;: &quot;INVALID_TARGET&quot;,<br>  &quot;attempts&quot;: 1<br>}</pre><p>Reason What your device store should do INVALID_TARGET Remove the rejected key PERMANENT_FAILURE Keep the key; investigate the message or credentials TRANSIENT_FAILURE Keep the key; attempts were exhausted, but the target may still work</p><p>A transient failure can have status: &quot;DEAD&quot;. That describes the exhausted delivery job. It doesn&#39;t establish that the device key is dead.</p><h3>Match the Key Your App Actually Stored</h3><p>Push.getPushKey() returns a full key such as cn1-fcm-example-device-token. The digest&#39;s token is only example-device-token. Comparing those strings deletes nothing. Matching device alone also misses legacy cn1-gcm- keys because the digest uses the canonical cn1-fcm- spelling.</p><p>Keep the original key for sending, and store a normalized provider and target alongside it. This registration helper handles Android and Apple mobile keys:</p><pre>function mobileTarget(pushKey) {<br>  const match = /^cn1-(gcm|fcm|ios|apns)-(.+)$/.exec(pushKey);<br>  if (!match) throw new Error(&#39;Expected an Android or Apple push key&#39;);<br>  const provider = {gcm: &#39;fcm&#39;, ios: &#39;apns&#39;}[match[1]] || match[1];<br>  return {provider, target: match[2], pushKey};<br>}</pre><p>Both cn1-gcm-abc and cn1-fcm-abc now match feedback for provider fcm, token abc. Apple keys with the ios prefix match provider apns. For web push, store the subscription&#39;s endpoint separately from its key material; don&#39;t treat the whole cn1-web-... key as an endpoint.</p><p><strong>Backfill these normalized columns for existing registrations before enabling cleanup.</strong> The examples below expect that schema. Scope lookups to the organization receiving the digest, and use both provider and target so identical strings from different providers don’t collide.</p><h3>Verify before interpreting the body</h3><p>The request carries:</p><pre>X-CN1-Signature: t=&lt;unix-millis&gt;,v1=&lt;hex&gt;</pre><p>The signature is HMAC-SHA256 over the timestamp, a period, and the raw body bytes. Use the secret from the Settings card, compare the MAC in constant time, and reject timestamps outside your allowed age window. Do not parse JSON and serialize it again before verification; whitespace and field ordering can change the signed bytes.</p><p>For the Codename One backend, the guide includes a <a href="https://github.com/codenameone/CodenameOne/blob/master/docs/demos/backend/src/main/java/com/codenameone/developerguide/backend/PushFeedback.java">complete Java receiver</a>. It uses the raw HttpServer.Request, Crypto.hmacSha256, and Crypto.equalsConstantTime, so it also fits native backend packaging. Its device store is an application-supplied interface, not an automatically provisioned database.</p><p>After verification, a CN1 backend receiver can apply each failure like this:</p><pre>// Inside the loop over the verified digest&#39;s event maps.<br>if (!&quot;INVALID_TARGET&quot;.equals(event.get(&quot;reason&quot;))) {<br>    continue;<br>}<br>String provider = (String) event.get(&quot;provider&quot;);<br>String target = (String) event.get(&quot;token&quot;);<br>if (target == null || target.length() == 0) {<br>    target = (String) event.get(&quot;endpoint&quot;);<br>}<br>if (provider == null || provider.length() == 0<br>        || target == null || target.length() == 0) {<br>    throw new IllegalArgumentException(&quot;Missing push target&quot;);<br>}<br>String deliveryId = (String) event.get(&quot;deliveryId&quot;);<br>String eventKey;<br>if (deliveryId != null &amp;&amp; deliveryId.length() &gt; 0) {<br>    eventKey = &quot;delivery:&quot; + deliveryId;<br>} else {<br>    Object at = event.get(&quot;at&quot;);<br>    if (at == null) {<br>        throw new IllegalArgumentException(&quot;Missing classic event timestamp&quot;);<br>    }<br>    eventKey = &quot;classic:&quot; + provider + &quot;:&quot; + target.length()<br>            + &quot;:&quot; + target + &quot;:&quot; + at;<br>}<br>if (!store.alreadyApplied(eventKey)) {<br>    store.removeTargetAndMarkApplied(eventKey, provider, target);<br>}</pre><p>Here store is an organization-scoped durable store with alreadyApplied(eventKey) and removeTargetAndMarkApplied(eventKey, provider, target) methods. The latter deletes by the normalized columns and writes the marker in one transaction. It must enforce a unique event key even if two requests pass the initial check concurrently.</p><p>The fallback key includes the provider, target, and event timestamp. It handles classic events without a deliveryId, distinguishes devices, and lets a later event for the same target be processed. A missing target or timestamp fails the request instead of acknowledging an event we couldn&#39;t correlate.</p><p>If a device has since registered a replacement key, remove the rejected key associated with the event. Don’t translate an old failure into “delete whatever key this user has now.”</p><h3>Acknowledge after the database commits</h3><p>Delivery is at-least-once. A lost HTTP response can cause a digest to arrive again even after you processed it.</p><p>Only a 2xx response advances the sender’s watermark. Return it after your writes are durable. If you return success first and the database write then fails, the acknowledged window won’t be resent. If the database commits and the response gets lost, the durable event-key marker lets the retry do nothing safely.</p><p>A digest has a size cap. truncated: true means another page follows in the same run, so the receiver must handle several requests close together. eventsOmitted counts accepted deliveries included only in the summary. It doesn&#39;t mean a set of unreported failure events has been lost.</p><p>A failing receiver is retried and eventually disabled after repeated failures. Check the reason on the Settings card and exercise the test event after fixing the endpoint. A receiver that has quietly stopped accepting feedback won’t keep your database clean.</p><h3>Use the backend you already run</h3><p>The <a href="https://www.codenameone.com/developer-guide/push-notifications/">push guide</a> includes CN1 backend, Spring, MicroProfile, and Node/serverless receiver examples. PHP and other stacks can implement the same signed JSON contract, but the guide doesn’t include a PHP receiver.</p><p>Here is the equivalent event-key helper for Node:</p><pre>function cleanupTarget(event) {<br>  const provider = event.provider;<br>  const target = event.token || event.endpoint;<br>  if (typeof provider !== &#39;string&#39; || !provider<br>      || typeof target !== &#39;string&#39; || !target) {<br>    throw new Error(&#39;Missing push target&#39;);<br>  }<br>  let eventKey;<br>  if (typeof event.deliveryId === &#39;string&#39; &amp;&amp; event.deliveryId) {<br>    eventKey = `delivery:${event.deliveryId}`;<br>  } else {<br>    if (!Number.isSafeInteger(event.at)) {<br>      throw new Error(&#39;Missing classic event timestamp&#39;);<br>    }<br>    eventKey = `classic:${provider}:${target.length}:${target}:${event.at}`;<br>  }<br>  return {eventKey, provider, target};<br>}</pre><p>For a concrete durable store, this Node example uses the built-in node:sqlite module. Run it on a Node version that provides that module. The devices table holds the normalized registration described above; populate it as devices register, and migrate existing keys before turning on the receiver.</p><pre>const {DatabaseSync} = require(&#39;node:sqlite&#39;);<br>const db = new DatabaseSync(&#39;push-feedback.sqlite&#39;);<br>db.exec(`<br>  CREATE TABLE IF NOT EXISTS devices (<br>    organization TEXT NOT NULL,<br>    provider TEXT NOT NULL,<br>    target TEXT NOT NULL,<br>    push_key TEXT NOT NULL,<br>    PRIMARY KEY (organization, provider, target)<br>  );<br>  CREATE TABLE IF NOT EXISTS applied_feedback (<br>    organization TEXT NOT NULL,<br>    event_key TEXT NOT NULL,<br>    PRIMARY KEY (organization, event_key)<br>  );<br>`);<br> <br>function applyInvalidTarget(organization, event) {<br>  if (event.reason !== &#39;INVALID_TARGET&#39;) return;<br>  const {eventKey, provider, target} = cleanupTarget(event);<br>  db.exec(&#39;BEGIN IMMEDIATE&#39;);<br>  try {<br>    const marker = db.prepare(`<br>      INSERT OR IGNORE INTO applied_feedback (organization, event_key)<br>      VALUES (?, ?)<br>    `).run(organization, eventKey);<br>    if (marker.changes) {<br>      db.prepare(`<br>        DELETE FROM devices<br>        WHERE organization = ? AND provider = ? AND target = ?<br>      `).run(organization, provider, target);<br>    }<br>    db.exec(&#39;COMMIT&#39;);<br>  } catch (error) {<br>    db.exec(&#39;ROLLBACK&#39;);<br>    throw error;<br>  }<br>}</pre><p>Use the configured organization’s ID after verifying the digest signature. Process its events through applyInvalidTarget, then return 2xx. On a database error, return a non-2xx response so the digest can be retried. The marker and deletion share a transaction: a failed deletion rolls the marker back too. A serverless deployment needs durable storage outside an ephemeral function filesystem; the local SQLite file above is for a server with persistent storage.</p><p><a href="https://github.com/codenameone/CodenameOne/pull/5890">PR #5890</a> adds those examples and documents the contract. Start with the test event, then send the same valid digest to your test receiver twice. Confirm that both requests get an appropriate response and only one durable cleanup operation occurs. Also test a failed database write: that request must not acknowledge a window it failed to apply.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e068d407279d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javarevisited/your-push-request-succeeded-the-device-key-is-dead-e068d407279d">Your Push Request Succeeded. The Device Key Is Dead.</a> was originally published in <a href="https://medium.com/javarevisited">Javarevisited</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Native Desktop Themes for Production Apps]]></title>
            <link>https://medium.com/javarevisited/native-desktop-themes-for-production-apps-ae35323bac91?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/ae35323bac91</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 15:24:53 GMT</pubDate>
            <atom:updated>2026-11-04T15:24:53.759Z</atom:updated>
            <content:encoded><![CDATA[<h4>Use native desktop themes with OS window integration, light and dark styles, and your own branding. See Fluent, Aqua, and Adwaita in the Settings app.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W8dyUhAsN0qPe1GuLbDAJA.jpeg" /></figure><p>A desktop app needs more than larger versions of its phone controls. Dialogs need their own windows. Title bars, scrollbars, and keyboard focus need to behave as desktop users expect.</p><p>Last week we introduced <a href="https://www.codenameone.com/blog/native-desktop-themes-experiment/">the native desktop theme experiment</a>. This week we bring those themes into production use. Fluent, Aqua, and Adwaita include refined controls in light and dark appearances, plus the window and dialog defaults that go with them. Your application can inherit those styles and retain its own branding.</p><p>The Codename One Settings tool now uses them too. Its main class selects @DesktopBuild(themeMode = &quot;native&quot;), choosing Fluent on Windows, Aqua on macOS, and Adwaita on Linux. Working through its forms and dialogs gave us concrete problems to fix in the themes. The screenshots below show the result, followed by the settings and CSS you can use in your own app.</p><h3>See the same application in each theme</h3><p>These are repository captures of the real Settings application. <strong>All six were made on a Mac.</strong> They show theme colors, borders, spacing, and light/dark styling. Fluent and Adwaita fall back to the Mac’s font in these captures; they are not screenshots of the application running on Windows or Linux. The respective platform fonts change the text on those hosts.</p><h3>Fluent</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XV9Qc4uFctJKo58cUJoxlw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GX5Y32Wvm7io2RrQ-9bNCw.png" /></figure><h3>Aqua</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_8qYgk8LVouWkR-kp69B0Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MymsUl1SsAGOLevkivQv0Q.png" /></figure><h3>Adwaita</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Z0iD4obqSDifpNWZ2JCMw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lzmnGyLnjhEQoE3usgicow.png" /></figure><p>The captures come from the <a href="https://github.com/codenameone/CodenameOne/blob/master/docs/developer-guide/Native-Themes.asciidoc">native-theme guide</a>. Their value is the complete screen: the theme has to make the relationships among controls readable, rather than make one isolated button resemble a reference tile.</p><h3>Opt in without repainting every control</h3><p>For a JavaSE desktop build, set this in the Settings app under <strong>Build Hints</strong>:</p><pre>desktop.themeMode=native</pre><p>native and auto select the host&#39;s theme. An explicit fluent, aqua, or adwaita selects that family even on another host. Unset remains legacy, preserving existing applications. On the separate native macOS port, Aqua is already the default when neither macos.themeMode nor the shared nativeTheme hint is set. You can select it explicitly with:</p><pre>macos.themeMode=native</pre><p>An explicit shared theme setting still applies: for example, nativeTheme=modern selects the iOS-style modern theme on this port. JavaSE desktop and native macOS are separate ports with different defaults.</p><p>In the simulator, select the Desktop skin, then choose a theme from <strong>Native Theme</strong>. With <strong>Auto</strong>, the Desktop skin follows desktop.themeMode, matching the packaged application&#39;s choice.</p><h3>Inherit the role and override the brand</h3><p>A custom UIID should begin with the native UIID whose behavior it refines. For example, in your application’s theme.css:</p><pre>/* Compiler fallbacks. Native theme palette values replace these at runtime. */<br>Container {<br>    --text-secondary-color: #5D5D5D;<br>    --text-secondary-color-dark: #CFCFCF;<br>}<br> <br>ProjectNameField {<br>    cn1-derive: TextField;<br>}<br> <br>ProjectPrimaryAction {<br>    cn1-derive: RaisedButton;<br>}<br> <br>ProjectCard {<br>    cn1-derive: GroupBox;<br>}<br> <br>ProjectHelp {<br>    cn1-derive: Label;<br>    color: var(--text-secondary-color);<br>}<br> <br>@media (prefers-color-scheme: dark) {<br>    ProjectHelp {<br>        color: var(--text-secondary-color-dark);<br>    }<br>}<br> <br>#Constants {<br>    includeNativeBool: true;<br>    --accent-color: #00796B;<br>    --accent-color-dark: #4DB6AC;<br>    --accent-fg-color: #FFFFFF;<br>    --accent-fg-color-dark: #102522;<br>    --accent-pressed-color: #00695C;<br>    --accent-pressed-color-dark: #26A69A;<br>    --selection-color: #00796B;<br>    --selection-color-dark: #4DB6AC;<br>}</pre><p>The Container declarations give the CSS compiler concrete values for var(). Keep these palette fallbacks outside #Constants: the compiler records their bindings, and the installed native theme supplies the runtime colors. Exporting the fallbacks as constants would override the platform palette.</p><p>The brand colors belong in #Constants because they should override the native palette. Both appearances need their own values, including the pressed and selection colors. The field and card inherit the platform&#39;s control treatments; ProjectHelp binds to its secondary text color in each appearance.</p><p>Assign the UIID in Java as usual:</p><pre>TextField projectName = new TextField();<br>projectName.setUIID(&quot;ProjectNameField&quot;);<br> <br>Button create = new Button(&quot;Create project&quot;);<br>create.setUIID(&quot;ProjectPrimaryAction&quot;);</pre><p>This doesn’t turn every component into an OS widget. Codename One still draws the controls. Theme inheritance lets the same application rules use each platform’s styles without copying its palette into the application.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/896/1*7-XyCpdLo30srA8URqsxig.png" /></figure><h3>A dialog should be a window when the desktop expects one</h3><p>The native desktop themes set defaultNativeWindowModeBool to true. An ordinary Dialog opens in a real operating-system window instead of appearing only as an overlay inside the application form. Its content remains CN1-rendered. Anchored popups such as combo boxes and context menus retain their popup behavior.</p><p>That distinction affects focus, ownership, placement, and keyboard handling. It is also why “native dialog” needs a precise meaning here: a native window contains the CN1 dialog; we aren’t claiming every dialog becomes an OS alert with OS-drawn controls.</p><p>Packaged JavaSE apps use native title bars by default with all three themes. The wrapper generator writes desktop.titleBar=native when the build hint is unset, and that property takes precedence over the theme constant. Set desktop.titleBar=custom explicitly if you want custom desktop chrome.</p><p>The simulator can expose a different default: without a title-bar property, it falls back to desktopTitleBarMode from the theme. That constant is native in Fluent and Aqua, and custom in Adwaita. An Adwaita simulator capture therefore doesn&#39;t establish the title bar a packaged app will use. The <a href="https://github.com/codenameone/CodenameOne/blob/master/maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/GenerateDesktopAppWrapperMojo.java">wrapper generator</a> controls that packaging behavior.</p><h3>Mouse behavior belongs in the theme too</h3><p>A desktop scrollbar needs a thumb you can grab, a track you can click, and space that doesn’t disappear while you’re aiming at it. The desktop themes enable interactive scrollbars with that behavior.</p><p>Hover is another platform decision. Fluent and Adwaita have their own pointer states. Aqua keeps ordinary button and field hover equal to normal because the AppKit reference does. Adding a highlight everywhere would make the theme less faithful, even if it looked more responsive in a demo.</p><p>JavaSE reads the desktop’s light/dark appearance at launch. The simulator has its own appearance menu. Don’t assume these captures establish live theme switching on every packaged desktop platform.</p><h3>Try a form with awkward content</h3><p><a href="https://github.com/codenameone/CodenameOne/pull/5886">PR #5886</a> connects the Settings application and simulator to the themes and records the six captures. The <a href="https://github.com/codenameone/CodenameOne/tree/master/native-themes">theme sources and behavior notes</a> show the UIIDs and constants the application inherits.</p><p>For your first screen, choose one with a long label, a validation error, and a dialog. Check keyboard navigation, resizing, and dark appearance. A compact component gallery can hide the spacing and window-ownership problems that an ordinary application exposes immediately.</p><p>This closes the <a href="https://www.codenameone.com/blog/who-decides-your-app-redesign/">weekly series</a>. The same choice runs through the iOS and desktop work: use the platform conventions deliberately, keep the application styling under your control, and test the result in a screen people actually need to use.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ae35323bac91" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javarevisited/native-desktop-themes-for-production-apps-ae35323bac91">Native Desktop Themes for Production Apps</a> was originally published in <a href="https://medium.com/javarevisited">Javarevisited</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Smaller, Faster Java Server with a Spring-Style API]]></title>
            <link>https://medium.com/javarevisited/a-smaller-faster-java-server-with-a-spring-style-api-9d57c846b5e0?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/9d57c846b5e0</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Mon, 02 Nov 2026 14:09:24 GMT</pubDate>
            <atom:updated>2026-11-02T14:09:24.523Z</atom:updated>
            <content:encoded><![CDATA[<h4>Build a smaller native Java service with Spring-style transactions and dependency injection. Inspect the generated code and compare local Linux startup, memory, throughput, binary size and build time.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F_nmO32QoLdsfTIiWT68nQ.jpeg" /></figure><p>What do you pay for Spring and Java’s dynamic architecture? Can you deliver the same API without the same runtime cost?</p><p>For a small service, you might want dependency injection, transactions and scheduled jobs without a long startup or a large deployment. That is the case our native Java backend is trying to make. Write a controller and a service, compile their wiring into the application, then deploy a native executable.</p><p>The HTTP runtime underneath that API is small. In the two-core Linux test below, its lower-level Bench handler occupies 5.17 MiB and reaches its first verified response in 14.8 ms. The same two endpoints in Spring/GraalVM occupy 88.31 MiB and respond in 43.6 ms. This handler uses no controller annotations or generated wiring, so those numbers measure the HTTP stacks, not the cost of the Spring-style API.</p><p>This week’s Spring-style API adds the parts that make that approach useful beyond a greeting endpoint: transaction propagation, scoped beans, background tasks, sessions, metrics and MCP tools. ParparVM supplies the native runtime, including virtual threads that can park while a handler waits for PostgreSQL, MySQL or another HTTP service.</p><p><strong>This is a Spring-style API, not a drop-in Spring implementation.</strong> Its annotations live in com.codename1.backend.annotations. Spring itself supports AOT processing and GraalVM Native Image. Our proposition is a smaller supported surface with direct generated calls, a native runtime shared with our app framework, and code you can inspect when you want to know what an annotation costs.</p><p>The backend is still a work in progress. It sits on ParparVM, the runtime behind our native iOS apps, and is maturing rapidly. The local measurements below show where it earns the “smaller, faster” description. They also define the narrow workload being compared.</p><h3>Coming This Week</h3><p>Today’s article covers the backend API, generated code and local server comparison. The follow-ups explore what you can do with the rest of the release:</p><ul><li><strong>Saturday, October 3:</strong> . How object headers, compiler optimizations and garbage collection affect the memory and CPU your application needs.</li><li><strong>Sunday, October 4:</strong> . Compare the videos, then see how measured motion and glass formulas make your UI feel closer to UIKit.</li><li><strong>Monday, October 5:</strong> . Start with fewer files, build a standalone backend, find the right Javadocs, and try the separate Android Gradle 9 option.</li><li><strong>Tuesday, October 6:</strong> . Connect your client app and native backend to traces across your enterprise services, with metrics and MCP alongside them.</li><li><strong>Wednesday, October 7:</strong> . Use desktop themes and visible menus in the browser. The Certificate Wizard gets the same theme families.</li><li><strong>Thursday, October 8:</strong> . Catch private API imports before submission, and what a difficult Friday release taught us about gaps in testing.</li></ul><h3>Compare startup, memory, throughput and build time</h3><p>The measurements use CN1 revision <a href="https://github.com/codenameone/CodenameOne/tree/60c4f310bddcfc51e5acc75c63a57c43c73430d3">60c4f310</a>. For the comparison, we ran two small HTTP handlers on the Linux deployment target: a 13-byte plaintext response and a JSON object containing one message field. The CN1 executable uses the repository&#39;s lower-level Bench handler. Spring Boot 4.1.1 uses Spring MVC and embedded Tomcat, running either on Temurin JDK 25 or as a GraalVM 25 native executable.</p><p><strong>This compares the selected HTTP stacks, not equivalent framework features.</strong> The CN1 handler does not exercise the annotation-generated application shown below. There is no database, TLS, authentication, or telemetry in either application. Both JSON handlers create a map per request; CN1 also allocates a fresh response object.</p><p>The host is an Apple M4 Max with 16 logical CPUs and 64 GiB RAM. The local Linux ARM64 VM has four virtual CPUs and 8 GiB. Linux CPU affinity restricts the server to 1, 2, or 4 CPUs. At 1 and 2, the load generator runs on the remaining CPUs; at 4, it shares them with the server. These are actual scheduling restrictions, not just a reported processor count.</p><h3>Start quickly and ship a smaller executable</h3><p>The two-core configuration gives the server and load generator separate CPUs. Here are medians from three fresh processes, with the startup range in parentheses:</p><p>Application First response, ms: median (range), lower is better Native executable, MiB, lower is better CN1 native 14.8 (5.3–15.1) 5.17 Spring Boot / JDK 25 1,501.1 (1,407.6–1,617.8) N/A Spring Boot / GraalVM 25 43.6 (32.5–45.6) 88.31</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lxep3IL7pJ9BUMBysyvj2g.png" /></figure><p>](https://www.codenameone.com/blog/native-http-charts/startup.png)</p><p>Startup includes spawning the process and receiving a verified HTTP response. File system caches are left warm; this is not a cold-machine or serverless measurement. The polling loop and taskset launcher also contribute to the short native times. The JVM run uses its default runtime settings without an AOT cache.</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UCj4vkLNAYP5lTq_kne2Zg.png" /></figure><p>](https://www.codenameone.com/blog/native-http-charts/binary-size.png)</p><p>The CN1 executable is 5.17 MiB, compared with 88.31 MiB for this Spring native build. These are uncompressed executable files from the default build outputs. Both files were unchanged by an additional pass that strips symbols. Both builds use shared system libraries, which are excluded here. This is not a container-size or complete-deployment comparison. The JVM column is N/A because its JAR is not a standalone native executable.</p><h3>Leave more memory for the work your service does</h3><p>A server’s memory use changes after it starts accepting traffic. We recorded all three stages using the same windows for each runtime:</p><p>Application, 2 allowed CPUs Idle before load, MiB Peak through load, MiB Idle after load, MiB CN1 native 10.0 57.8 29.8 Spring Boot / JDK 25 163.0 282.2 282.3 Spring Boot / GraalVM 25 78.1 117.0 79.6</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lv8Y7_JZXrLLA4gkr5aq9w.png" /></figure><p>](https://www.codenameone.com/blog/native-http-charts/memory.png)</p><p><em>Lower is better in all three memory columns. Idle before load is sampled three seconds after the first verified response. Peak is the kernel’s resident-memory high-water mark through startup and both routes. Idle after load is sampled five seconds after traffic ends, without forcing collection. Each table entry is the median of the three process measurements.</em></p><p>CN1 uses less resident memory in both the idle and loaded measurements here. Four-byte object metadata is only one part of that cost. RSS also includes code, stacks, allocator pages and library state; it is not the live Java heap. This Linux build uses native virtual threads, so its connection costs differ from a platform-thread pool.</p><h3>Serve requests with 1, 2 or 4 CPUs</h3><p>Allowed server CPUs Application Plaintext requests/s JSON requests/s 1 CN1 native 164,542 138,555 1 Spring Boot / JDK 25 34,721 64,068 1 Spring Boot / GraalVM 25 40,286 39,167 2 CN1 native 287,997 261,121 2 Spring Boot / JDK 25 69,953 70,491 2 Spring Boot / GraalVM 25 51,729 49,000 4 CN1 native 245,608 251,435 4 Spring Boot / JDK 25 116,491 110,530 4 Spring Boot / GraalVM 25 80,053 61,873</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Nki2EiUOlFjV2ugyXFuuow.png" /></figure><p>](https://www.codenameone.com/blog/native-http-charts/throughput.png)</p><p><em>Higher is better. Points are medians; whiskers show the three-run range. Each route has ten seconds of warmup and ten seconds of measurement, using two </em><em>wrk threads and 32 connections. Plaintext precedes JSON. Four-core points include load-generator contention, so this is not a clean four-core scaling curve.</em></p><h3>Does HotSpot catch up after warmup?</h3><p>CN1 still leads after a minute of warmup per endpoint: <strong>3.9 times the plaintext throughput and 3.4 times the JSON throughput</strong> of Spring/JDK 25 in the matched two-core follow-up. Both servers received the same longer warmup, followed by three consecutive ten-second measurement windows for each route.</p><p>Application, 2 allowed CPUs Plaintext requests/s JSON requests/s CN1 native, 60 s warmup per route 296,928 250,268 Spring Boot / JDK 25, 60 s warmup per route 75,721 73,613</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1iRFnVFXe0anlVF5FWEZnQ.png" /></figure><p>](https://www.codenameone.com/blog/native-http-charts/warmed-throughput.png)</p><p><em>Higher is better. These are medians of three windows within one fresh process per runtime, separate from the three-process matrix above. The server uses CPUs 0 and 1; </em><em>wrk uses CPUs 2 and 3, with 32 connections. Both routes completed without socket or non-2xx errors.</em></p><p>We checked HotSpot’s compiled-method list during warmup: Spring’s request dispatcher and handler adapter had reached tier-4 compilation. The snapshot is included in the reproduction bundle. The extra warmup does not close the gap for these two handlers. More complex application code, dependencies and different concurrency levels can change that comparison.</p><p>The matrix rotates runtime order and reverses CPU order in its middle round. Every response body is checked before load; any socket or non-2xx error during warmup or measurement rejects the run. All 27 process runs completed without those errors. Keep-alive request limits are raised or disabled on both servers; other runtime settings use their defaults.</p><h3>How long do you wait for a build?</h3><p>Native compilation moves work out of startup and into the build. You do not need to pay that cost after every edit: CN1’s debug/development mode runs on the JVM, while the native build produces the deployment executable.</p><p>Build Median elapsed, seconds Three-run range, seconds Spring Boot / JDK 25 2.07 1.54–2.57 CN1 debug / JVM 1.67 1.57–2.49 CN1 native 95.34 75.43–107.08 Spring Boot / GraalVM 25 200.89 174.80–214.39</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_kQ12fUemxkkPtw6nw-Owg.png" /></figure><p>](https://www.codenameone.com/blog/native-http-charts/build-time.png)</p><p><em>Lower is better. Three builds per configuration, with runtime order rotated, on the same four-vCPU, </em><em>8 GiB Linux VM. Application outputs are rebuilt each time; dependencies and toolchains are cached. Downloads, tests and server startup are excluded.</em></p><p>For this example, the CN1 native build takes <strong>53% less elapsed time</strong> than Spring/GraalVM. The JVM builds take seconds. That keeps the edit-and-debug loop short while letting you check the native artifact before deployment.</p><p>The CN1 development measurement compiles the repository’s Bench handler, JavaSE backend runtime and shared sources with JDK 25. The native path compiles Java with JDK 8, translates it, then compiles and links the generated C with Clang at -O3; the translator and Java API dependencies are prebuilt. Spring uses Maven clean package on JDK 25; its native build adds Spring AOT processing and GraalVM native:compile. These are build-path timings for the HTTP examples, including their different build tools, rather than isolated javac or incremental-build timings.</p><p>The <a href="https://www.codenameone.com/blog/native-http-comparison.zip">reproduction bundle</a> includes sources, commands, artifact hashes, every raw sample, the matched longer-warmup comparison, clean-output build timings, and the controller and transaction examples. The results make a small native backend worth trying for services with these constraints. Measure your own handlers and dependencies before using them to choose deployment capacity.</p><h3>Start your service with less runtime setup</h3><p>Here is a complete controller for a backend module:</p><pre>package example;<br> <br>import com.codename1.backend.annotations.GetMapping;<br>import com.codename1.backend.annotations.RestController;<br> <br>@RestController<br>public class GreetingApi {<br>    @GetMapping(&quot;/hello&quot;)<br>    public String hello() {<br>        return &quot;Hello, World!&quot;;<br>    }<br>}</pre><p>After javac compiles your sources, the build reads the annotations and generates Java classes for routing, dependency injection and method instrumentation. ParparVM translates the resulting bytecode to C, then the native compiler builds the executable.</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*SY1d2n0aJPd7piQ3OAMtFg.png" /></figure><p>](https://www.codenameone.com/developer-guide/img/backend-build-pipeline.svg)</p><p><em>From the current Backend guide. The generated classes are ordinary input to the translator and its dead-code analysis.</em></p><p>A generated Router holds the route as bytes and binds parameters according to their declared types. BackendWiring creates beans in dependency order. Missing dependencies, ambiguous candidates, constructor cycles and duplicate routes fail the build with the relevant class or injection point.</p><p>The wiring constructs this controller and the Notes service in the next section with direct calls like these:</p><pre>b_greetingApi = new example.GreetingApi();<br>b_notes = new example.Notes(<br>        com.codename1.backend.Backend.requireDataSource(<br>                dataSource, &quot;constructor parameter 1 of example.Notes&quot;));</pre><p>There is no runtime search for a Notes constructor or a matching database bean. The call is in the generated program. With more dependencies, the build orders the constructor calls and emits field or setter injection afterward. A private @Autowired field gets a generated setter so the assignment can still be a direct call.</p><p>This gives you a useful debugging option: follow the call into the generated code and see exactly what it constructs. It also lets the native linker remove unreferenced classes. You exchange some of Spring’s runtime flexibility for a build that can resolve more of the application before deployment.</p><h3>Keep your transaction when you call another method</h3><p>If you use Spring’s usual proxy-based transactions, a call through this bypasses the proxy. That can make a refactor change transactional behavior even though the annotation is still on the method. Spring documents both the limitation and its bytecode-weaving alternative in the <a href="https://docs.spring.io/spring-framework/reference/core/aop/proxying.html">proxying guide</a>.</p><p>Our build rewrites the annotated method itself. @Transactional, @Async, @Timed and @Counted therefore apply when another method on the same object calls it. They also apply to private methods and project classes you construct with new.</p><p>For example, this service writes a note and an audit row in one transaction. Assume migrations have created note(body) and audit(message) in the configured database:</p><pre>package example;<br> <br>import com.codename1.backend.DataSource;<br>import com.codename1.backend.annotations.Component;<br>import com.codename1.backend.annotations.Transactional;<br>import java.io.IOException;<br> <br>@Component<br>public class Notes {<br>    private final DataSource db;<br> <br>    public Notes(DataSource db) {<br>        this.db = db;<br>    }<br> <br>    @Transactional(rollbackFor = IOException.class)<br>    public void add(String body) throws IOException {<br>        db.execute(&quot;INSERT INTO note (body) VALUES (?)&quot;, new Object[] {body});<br>        db.execute(&quot;INSERT INTO audit (message) VALUES (?)&quot;,<br>                new Object[] {&quot;note created&quot;});<br>    }<br>}</pre><p>What does the annotation become? The build moves the original method body into a separate method and wraps its call in transaction handling. This pseudocode shows the control flow for Notes.add; helper names and generated class layouts are implementation details:</p><pre>transaction = begin(REQUIRED, readOnly = false, timeout = none)<br>try:<br>    call original add(body)<br>catch error:<br>    rollback = error is IOException<br>            or error is RuntimeException<br>            or error is Error<br>            or error is DataAccessException<br>    completeAfterFailure(transaction, rollback)<br>    rethrow error<br>commit(transaction)</pre><p>The body runs inside a transaction boundary regardless of how you reached add. On failure, the wrapper applies the rollback rule to that boundary and rethrows the error. Completing a joined transaction can mark the outer transaction for rollback; it does not necessarily commit or roll back the connection immediately. There is no runtime annotation lookup or proxy dispatch for this method. The <a href="https://github.com/codenameone/CodenameOne/blob/master/maven/build-engine/src/main/java/com/codename1/maven/processors/BackendSources.java">processor source</a> and <a href="https://github.com/codenameone/CodenameOne/blob/master/maven/build-engine/src/main/java/com/codename1/maven/processors/BackendWeaver.java">bytecode weaver</a> show how this is implemented.</p><p>The database connection is borrowed lazily, at the first database operation. The method can return without touching the pool if it never accesses the database. DataSource, generated DAOs and managed ORM sessions join the transaction associated with the executing thread.</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*zw-J3jA8B2lpxRwIaOzp5A.png" /></figure><p>](https://www.codenameone.com/developer-guide/img/backend-transaction-propagation.svg)</p><p>You can choose all seven propagation modes: REQUIRED, REQUIRES_NEW, NESTED, SUPPORTS, MANDATORY, NOT_SUPPORTED and NEVER. The important choices are what you want to happen when a transaction exists, and whether an inner failure should undo the outer work. REQUIRES_NEW needs another connection; NESTED uses a savepoint on the current one.</p><p>Read-only declarations, timeouts and explicit rollback rules are supported. Isolation-level selection is not. An asynchronous task does not inherit its caller’s transaction, and a database transaction cannot undo an email or an HTTP request. The <a href="https://www.codenameone.com/developer-guide/backend-data/">data chapter</a> spells out these boundaries, including what happens when code touches more than one connection pool.</p><h3>Handle more waiting requests without a thread per connection</h3><p>If your handler spends most of its time waiting for another server, an OS thread per connection is expensive. You want straightforward blocking code without making each wait occupy a host thread.</p><p>On the Linux native HTTP path, each connection has a ParparVM virtual thread: a stack retained for the life of that connection. Host threads run those stacks. A host owns its connections and their epoll registrations; a virtual thread stays with that host. When the handler needs bytes that have not arrived, the runtime parks its stack and lets the host run another connection.</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/770/1*s_TwsEW5GEPj-cbdADSzug.png" /></figure><p>](https://www.codenameone.com/developer-guide/img/backend-architecture.svg)</p><p>That applies beyond the incoming socket. PostgreSQL and MySQL queries use the same parking mechanism. Outbound HTTP, including its TLS handshake, can park too. Your service can wait for a query in ordinary sequential Java while other connections continue on the same host.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*UcmOrUBwCCHUT1tGBi_npw.png" /></figure><p>Background tasks now participate in this model too. A virtual task has no incoming connection descriptor, so the host uses a wake pipe to notice newly submitted work. Future.get() on a virtual thread yields cooperatively while waiting for the task. Blocking the host there could otherwise stall the very task whose result the handler needs.</p><p>This work also fixed a subtle scheduler failure: each yield site must report whether it is runnable or waiting for I/O. A stale reason could leave a handler stuck after yieldNow() or send an I/O wait back through the runnable path after garbage-collector backpressure. A virtual-thread API is only useful when those transitions remain correct under load.</p><p>You still need to choose the right executor for your work:</p><p>Your operation Native virtual-thread behavior Practical choice Wait for PostgreSQL, MySQL or outbound HTTP Parks at supported socket waits Virtual threads suit this work Wait on an asynchronous task’s Future Yields cooperatively A virtual handler can await virtual work SQLite, file access, DNS resolution or Object.wait() Can block the host thread Put blocking local work on a platform executor Incoming TLS server connection Uses the platform-thread pool Account for pool capacity JVM development run Uses platform threads, including VIRTUAL tasks Test native concurrency before deployment</p><p>These are ParparVM’s virtual threads, not the JDK implementation. ThreadKind.PLATFORM remains the default for @Async and @Scheduled; VIRTUAL requests the native facility where available, and AUTO selects it when the server has virtual hosts. The <a href="https://www.codenameone.com/developer-guide/backend-scheduling/">scheduling chapter</a> documents fallback behavior and named executors.</p><h3>Schedule work without accidental duplicate runs</h3><p>A scheduled method runs outside the request thread. Use a fixed rate when you want starts at regular intervals, or a fixed delay when you want a pause after the previous run finishes. The distinction becomes visible as soon as the job takes longer than expected:</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*W5cKE0qAXzcPtpSs1sTeAA.png" /></figure><p>](https://www.codenameone.com/developer-guide/img/backend-schedule-timelines.svg)</p><p>One job does not overlap itself within a process. If a fixed-rate job is still running at its next start, that start is skipped. The build validates the method signature and six-field cron expressions; configuration can supply the actual schedule.</p><p>When you deploy several server instances, each instance has a scheduler. A database lock lets them coordinate one job across replicas. For example, @Scheduled(fixedDelay = 60000, lock = &quot;purge&quot;) requests a shared lock. The lease must exceed the job&#39;s maximum runtime, since expiry permits another instance to claim it. You can inspect job state and skipped runs through the management endpoints.</p><h3>Keep configuration and state explicit as your service grows</h3><p>The Spring-style surface extends beyond the three annotations in a demo:</p><p>What you need API and behavior Select an implementation @Primary and @Qualifier resolve competing dependencies Bind deployment settings @Value and @ConfigurationProperties bind configuration Enable an environment-specific bean @Profile and @ConditionalOnProperty control activation Construct a library object A @Bean factory supplies it explicitly Keep state for one request or session @Scope(&quot;request&quot;) and @Scope(&quot;session&quot;) locate the current instance through generated stand-ins Initialize and shut down resources @PostConstruct and @PreDestroy hooks participate in lifecycle management Measure or manage a service method @Timed, @Counted, @ManagedResource and related annotations generate adapters</p><p>The build catches unresolved wiring, but runtime configuration can still deactivate a required bean or omit a necessary value. Request, session and lazy stand-ins need a non-final class and an accessible no-argument constructor. That constructor runs for the stand-in too. The <a href="https://www.codenameone.com/developer-guide/backend-beans/">beans chapter</a> explains the supported combinations and their restrictions.</p><p>Sessions use an in-memory or database store. They are created on demand, so an API that never asks for a session does not set a session cookie. Rotate the identifier at sign-in with changeSessionId(), and use the database store when multiple instances need to share session state. Session beans run their @PreDestroy or configured destroy method when the session is invalidated, found expired or closed at server shutdown. Expiry cleanup happens when a request encounters the expired session or triggers the periodic purge, rather than at the exact timeout instant.</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*hpNUthI8ammXxZqD6OksUQ.png" /></figure><p>](https://www.codenameone.com/developer-guide/img/backend-session-lifecycle.svg)</p><p>There is no general ApplicationContext lookup, and an arbitrary JAR on the classpath does not become a compatible Spring bean library. Native backend sources currently use Java 8 language level against the supported runtime library. Packaged deployment targets Linux. Those boundaries help you decide whether a small service fits this backend before attempting a migration.</p><h3>Keep your service visible across your enterprise</h3><p>An order can cross a client app, your native backend and several existing services. The backend can continue the incoming trace and propagate it on outbound HTTP calls so your operations team can follow that work in its existing observability system. Optional OpenTelemetry tracing and metrics are compiled into the executable, alongside management endpoints for health, jobs and application resources.</p><p>The generated server has a defined request chain. Its own endpoints run before application routes, and static files come last. A wrapper handles request scope, session persistence and request metrics:</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*ZPR7s7yTiEQVpG2OKLMTpA.png" /></figure><p>](https://www.codenameone.com/developer-guide/img/backend-request-lifecycle.svg)</p><p>MCP gives your development tools a way to inspect the running service. An agent can list routes and beans, call an endpoint, inspect recent requests, and query database state through the development tools. You can publish application tools with @McpTool; the build generates their argument schemas and direct dispatch.</p><p>Development tools are excluded from packaged servers by default. A production MCP endpoint requires a token. Tuesday’s article connects the client and backend to an enterprise trace, then covers metrics and the MCP development workflow.</p><p>To try the backend, start with the <a href="https://www.codenameone.com/developer-guide/backend/">backend guide</a> and a small endpoint, then add a database operation and verify its rollback behavior. The implementation behind <a href="https://github.com/codenameone/CodenameOne/pull/5908">PR #5908</a> is the basis for this week’s expanded API.</p><h3>More This Week</h3><p>That is the backend story. The rest of the release reaches your app’s UI, project structure and submission process. Here is a closer look at each upcoming post, plus a small maps improvement.</p><h3>How your Java server uses less RAM</h3><p>**Saturday: **</p><p>The server comparison above puts CN1’s peak RSS at <strong>57.8 MiB</strong>, against <strong>117.0 MiB</strong> for Spring/GraalVM. Saturday explains the runtime work behind that smaller footprint: four-byte object headers, tighter fields, fewer temporary objects and changes to garbage collection. We will also look at the compiler translating itself faster than JDK 25, and explain how ParparVM&#39;s layout differs from HotSpot&#39;s compact headers and Leyden&#39;s AOT work. Headers are part of the saving; the whole server measurement includes code, stacks and libraries too.</p><h3>Can your users spot the custom-rendered tab bar?</h3><p>**Sunday: **</p><p><a href="https://www.codenameone.com/blog/ios-27-glass-you-can-test/">Last week’s iOS 27 theme</a> still had motion I wasn’t happy with. This comparison shows the refinement under taps, holds, and drags. Watch the original video; the small changes in timing and glass detail are the point.</p><p><a href="/https/medium.com/blog/ios27-measured-glass/dark-tabs.mp4">Watch the tab bar comparison</a>.</p><p><em>UIKit is above Codename One in this side-by-side simulator recording.</em></p><p>Sunday’s post follows the reverse engineering from frame measurements to spring equations, vibrancy and lens rendering. You can judge the remaining differences and choose when to adopt the theme in your app.</p><h3>Choose Gradle and start with fewer files</h3><p>**Monday: **</p><p>You can now start a smaller Gradle project and add platform directories when needed. A standalone backend no longer requires a client project alongside it. That makes the tree easier to navigate and leaves less boilerplate in an LLM’s context. I still prefer Maven XML, for reasons involving the make files I lived with at Sun, but your preference should not block either project shape.</p><p>We converted the repository’s HelloCodenameOne sample: <strong>358 files became 305</strong>, and <strong>nine POMs became two Gradle Kotlin files</strong>. That is 53 fewer files around the same application. The count includes sources and resources, excludes generated build output, and comes from the actual converter.</p><p>The generated settings.gradle.kts selects the build plugin:</p><pre>plugins {<br>    id(&quot;com.codenameone&quot;) version &quot;8.0-SNAPSHOT&quot;<br>}<br>rootProject.name = &quot;HelloCodenameOne&quot;</pre><p>This is an excerpt; the generated file also configures plugin repositories. Use the release version containing Gradle support when it is published. Monday shows the full setup and conversion commands.</p><p>The new <a href="https://www.codenameone.com/backend/javadoc/">backend Javadocs</a> also separate server APIs from the <a href="https://www.codenameone.com/javadoc/">client reference</a>, with shared classes identified between them. Monday covers that split and the separate, experimental Gradle 9 Android build option. Choosing Gradle for your project and changing the Android builder’s Gradle version are independent choices.</p><h3>Bring your app into your enterprise traces</h3><p>**Tuesday: **</p><p>Your mobile or web app can be the start of a trace that crosses a native CN1 backend, existing Java services and a database. The client and server propagate the same trace context, so compiling the backend to native code need not leave a gap in your enterprise observability. You can use your existing OTel collector and tracing system, with collector credentials kept on the server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kW4dLo-w8aqOqXpPzPDzAw.png" /></figure><p>Tuesday explains the setup, sampling and metric labels that keep telemetry manageable as traffic grows, plus MCP tools for checking the service during development. Downstream services must participate in trace propagation too.</p><h3>Make your browser app feel like a native desktop app</h3><p>**Wednesday: **</p><p>Your web app can now use native desktop OS styling: macOS (Aqua), Windows (Fluent) or Linux (Adwaita). An optional HTML title and menu bar puts commands above the application. Additional OS windows remain unsupported in the JavaScript port because of browser popup restrictions; that does not exclude Windows browsers. The Certificate Wizard adopts the same desktop theme families, continuing last week’s Settings work.</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3CtufMUyxSmOFUVUbvqg6Q.jpeg" /></figure><p>](https://www.codenameone.com/blog/desktop-theme-collage.jpg)</p><p><em>The Settings app shows the three desktop theme families now available in the JavaScript port.</em></p><h3>Read street names along the road</h3><p>There is a smaller maps improvement too: names now follow the road geometry, with curved glyph placement on bends and upright text along straighter sections. Labels remain above route overlays, so the route does not hide the street you need to follow.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/786/1*hULTeB8ZdLrumFu2_ljuuQ.png" /></figure><p><em>From the Maps guide. The renderer rejects placements that bend too sharply or collide with other labels.</em></p><p>This is part of the continuing vector-map work in <a href="https://github.com/codenameone/CodenameOne/pull/5902">PR #5902</a>. The <a href="https://www.codenameone.com/developer-guide/maps/">Maps guide</a> covers the renderer and deeper zooming without stretching raster tiles.</p><h3>Your release also has to survive submission</h3><p>**Thursday: **</p><p>A working simulator or device build can still import an API that Apple rejects at submission. In this case, the unwanted imports appeared only when particular optional APIs were enabled, so most apps and our usual test configuration missed them. The new check looks for imported SDK symbols without public declarations, exercises optional feature combinations and inspects a linked Release device app. It catches a class of mistake that another happy-path test would miss.</p><p>This was a rough week of regressions around the optional Xcode 27 path, push, and windows. Some overlapped, so we thought we had fixed a bug only to discover its symptoms had moved. Thursday’s post explains one of those failures, why we release on Friday despite the obvious cost to our weekends, and how better gates can protect your release from ours. The Apple check has a defined scope; it cannot guarantee App Store acceptance.</p><p>There is a lot to try this week. There is also work to do on the reliability of getting it into your hands. I would like you to spend the next release testing something useful in your app, rather than helping us untangle a regression. That is why the gate work matters as much as the new features.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9d57c846b5e0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javarevisited/a-smaller-faster-java-server-with-a-spring-style-api-9d57c846b5e0">A Smaller, Faster Java Server with a Spring-Style API</a> was originally published in <a href="https://medium.com/javarevisited">Javarevisited</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Honey, I Shrunk Java]]></title>
            <link>https://medium.com/javarevisited/honey-i-shrunk-java-7de1f64f853d?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/7de1f64f853d</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Mon, 02 Nov 2026 14:09:17 GMT</pubDate>
            <atom:updated>2026-11-02T14:09:17.949Z</atom:updated>
            <content:encoded><![CDATA[<h4>ParparVM translates its compiler with 37.5% less time and 45.8% less peak memory in the Linux ARM64 baseline. See the object, compiler and GC changes behind it.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2V9viszI_UZNT-0_HUc3YQ.jpeg" /></figure><p>The current Linux ARM64 performance baseline has ParparVM translating its own compiler in <strong>37.5% less elapsed time and 45.8% less peak memory than JDK 25</strong>. Across all 16 recorded machine configurations, self-translation takes <strong>22.5% to 56.6% less elapsed time</strong>. The gains vary by OS and processor; the chart and table include every configuration. Your Java source does not need to change to benefit from a smaller runtime and a compiler that removes more work.</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xq0hmd3VFEVM5K1mEDAbGw.png" /></figure><p>](https://www.codenameone.com/blog/runtime-diagrams/translation-baselines.png)</p><p>These are the repository’s <a href="https://github.com/codenameone/CodenameOne/blob/60c4f310bddcfc51e5acc75c63a57c43c73430d3/vm/selfhost/perf-baseline.json">checked-in performance baselines</a>, read on October 1. They are recorded reference results used to detect regressions, not new measurements made for this article. Each comparison runs the same translator classes on the two runtimes and verifies the generated files byte for byte.</p><p>Self-translation baseline Elapsed time, ParparVM / JDK 25 Peak memory, ParparVM / JDK 25 Recorded calibration runs Linux ARM64, Neoverse N2 0.625 0.542 7 Linux x64, EPYC 7763 0.484 0.519 3 Linux x64, EPYC 9V45 0.495 0.539 1 Linux x64, EPYC 9V74 0.516 0.522 2 Linux x64, Xeon 6973P-C 0.537 0.520 1 Linux x64, Xeon Platinum 8370C 0.434 0.509 3 Linux x64, Xeon Platinum 8573C 0.505 0.517 1 macOS ARM64 0.539 0.739 1 Windows ARM64, model D49 0.751 0.539 7 Windows ARM64, model D84 0.775 0.570 1 Windows x64, AMD family 25/model 1 0.606 0.542 7 Windows x64, AMD family 25/model 17 0.636 0.551 2 Windows x64, AMD family 26/model 2 0.611 0.546 1 Windows x64, Intel family 6/model 106 0.627 0.536 1 Windows x64, Intel family 6/model 173 0.677 0.549 1 Windows x64, Intel family 6/model 207 0.602 0.526 1</p><p><em>Lower is better in both columns. JDK 25 is 1.0. Each baseline is the median of the recorded runs’ median ratios, with each platform using its available CPUs and runtime defaults. Translation time includes the whole process; Linux and macOS memory is peak RSS; Windows memory is peak working set. Some configurations have only one calibration run, as shown. They are separate machines, not a core-count scaling test.</em></p><p>A couple of weeks ago I wrote about <a href="https://www.codenameone.com/blog/parparvm-compiles-itself/">making ParparVM compile itself</a>. HotSpot beat us on elapsed time. That was disappointing, though hardly surprising. Since then we have worked on object layout, collections, generated C and garbage collection. The results above are a much better place to be.</p><p>The smaller objects matter beyond the compiler benchmark. In <a href="https://www.codenameone.com/blog/java-server-work-before-startup/">Friday’s server comparison</a>, CN1’s native backend peaks at <strong>57.8 MiB RSS</strong>, against <strong>117.0 MiB</strong> for Spring/GraalVM, and rests at <strong>29.8 MiB</strong> after load against <strong>79.6 MiB</strong>. That compares complete HTTP stacks. Here we will look at the runtime changes that help make a smaller Java process possible.</p><h3>What gets faster, and what still needs work?</h3><p>Self-translation is a substantial Java workload: allocating objects, walking collections, resolving methods and writing generated C. It is also our own compiler, so we should check other workloads before treating it as a verdict on Java performance.</p><p>The complete Linux ARM64 baseline contains 13 workloads. ParparVM uses less peak memory in all 13 and less elapsed time in eight. Sequential array access takes 0.362 times the JDK’s measured time; hash-map churn uses 0.121 times its peak process memory. Allocation remains an important weakness: that workload takes 3.043 times the JDK’s time, even while using 0.239 times its peak memory.</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7zS9kljFEEpTKfJ89iuuuA.png" /></figure><p>](https://www.codenameone.com/blog/runtime-diagrams/arm64-baselines.png)</p><p><em>Each row combines seven calibration runs; each run compares the two runtimes in alternating order. Translation rows measure the complete process. The smaller workloads time repetitions inside the process after their own warmup, so JVM startup does not count against HotSpot. Memory is whole-process peak RSS in every row, not live heap.</em></p><p>The <a href="https://github.com/codenameone/CodenameOne/blob/master/vm/selfhost/perf-gate.py">performance harness</a> checks workload results as well as time and memory. The original self-hosting post measured 1.56 seconds for HotSpot against 1.84 seconds for ParparVM. The opening charts show the later checked-in baselines after the optimization work; they are separate measurements, not a reinterpretation of that earlier result.</p><p>Elapsed time and CPU time also answer different questions. Parallel JIT compilation and collection can reduce the wait while consuming more CPU across threads. That distinction helped guide the investigation, but it does not establish a battery saving: energy depends on the machine’s power use over time. The current baseline records elapsed time and peak memory, so those are the claims we can make from it.</p><h3>Why a tiny Java object needs a header</h3><p>An object header is runtime metadata stored alongside your fields. It lets the runtime identify the object’s class and track information needed for collection and other operations. On a typical 64-bit HotSpot configuration, the header occupies 12 bytes; without compressed class pointers it can occupy 16. JDK 25’s optional compact object headers reduce that to 8 bytes. <a href="https://docs.oracle.com/en/java/javase/25/gctuning/other-considerations.html">Oracle’s JDK 25 GC guide</a> documents the sizes and the -XX:+UseCompactObjectHeaders switch.</p><p>This is the work associated with <strong>Project Lilliput</strong>. <strong>Project Leyden</strong> addresses startup, warmup and footprint through ahead-of-time work, including AOT caches. The names are easy to mix up, but shrinking each object’s header and caching class-loading work solve different problems. See the <a href="https://openjdk.org/projects/leyden/">Leyden project</a> for that distinction.</p><p>Runtime layout Object header metadata What the number excludes HotSpot, compressed class pointers 12 bytes Fields, alignment and backing storage HotSpot, JDK 25 compact headers enabled 8 bytes Fields, alignment and backing storage ParparVM release layout 4 bytes Fields, alignment, side tables and backing storage</p><p>These are layout sizes, not a prediction of process memory. A small HTTP server can spend more of its resident memory on stacks, code, allocator pages and library state than on live Java object headers. The <a href="https://www.codenameone.com/blog/java-server-work-before-startup/">server comparison</a> measures the whole process separately.</p><p>The release object header carries a 16-bit class index, an 8-bit collection epoch and an 8-bit heap state. The class index addresses a closed-world class table instead of storing a pointer in every object. Objects requiring a legacy heap index use a side table keyed by address.</p><p>[</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*LboMlHUATCNqIb29pjMkqg.png" /></figure><p>](https://www.codenameone.com/blog/runtime-diagrams/object-header.svg)</p><p>The <a href="https://github.com/codenameone/CodenameOne/blob/master/vm/ByteCodeTranslator/src/cn1_globals.h">runtime header</a> is explicit about the distinction between metadata and allocation. The C header struct remains eight-byte aligned. Small objects live in allocator size classes, and fields need alignment too. A four-byte header does not make an empty object a four-byte allocation.</p><p>The translator packs eligible fields into the gap after the metadata. It stores fields at their real widths and orders them to reduce padding. A subclass must preserve its superclass layout as a prefix, so it cannot arbitrarily reach back and fill a hole belonging to an ancestor. That restriction sharply reduced the savings predicted by an early census.</p><p>Object in the experiment Before After four-byte header and root-field packing VarOp 32 bytes 24 bytes ArrayList 32 bytes 24 bytes ByteCodeMethodArg 32 bytes 24 bytes BasicInstruction 40 bytes 32 bytes</p><p><em>Object sizes from the layout experiment. Backing storage is additional.</em></p><p>The <a href="https://github.com/codenameone/CodenameOne/blob/60c4f310bddcfc51e5acc75c63a57c43c73430d3/vm/selfhost/experiments/REGISTRY.md#round-49-the-4-byte-header-and-why-it-is-worth-much-less-than-the-8-byte-one">header-layout experiment</a> measured one-core container RSS moving from 618 MB to 605 MB across five interleaved runs. That is useful, but much smaller than “we halved the header” might suggest. The page allocator and native collection buffers still occupy memory.</p><p>There was a second trap. A static class table that named every class also kept those classes reachable to the native linker. The current implementation registers most entries as classes are used. Shrinking object metadata should not accidentally force otherwise unused classes into the executable.</p><h3>Get less overhead from the same Java loop</h3><p>ParparVM translates bytecode to C. “Lowering” is the step that turns a higher-level operation into a representation the native compiler can optimize directly. We do not get that benefit merely by giving Clang a large pile of C functions.</p><p>Consider a collection traversal. The ordinary implementation has an iterator object and virtual calls for hasNext() and next(). When the translator proves the exact collection layout, it can emit a native cursor loop. When it cannot prove the receiver, it can check the exact class once and retain the ordinary iterator as a fallback.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/495/1*llDGF-8o4WI6Nglj6eRYjw.png" /></figure><p>The owner must remain a GC root until the final native-buffer access. A raw pointer into a malloc block does not keep the Java owner alive. The emitted lifetime fence is therefore part of correctness, not optional bookkeeping to strip from a fast loop.</p><p>Several related changes attack allocation and dispatch:</p><ul><li><strong>Collection storage:</strong> hash tables combine key, value and metadata slices into one native allocation. Reference slices still need tracing and write barriers. Moving them outside the managed heap does not make their Java references invisible to the collector.</li><li><strong>Strings and builders:</strong> StringBuilder keeps Latin-1 bytes until it needs UTF-16. Proven temporary builders can use native storage while preserving ownership across helper calls and exceptions. StringBuffer retains its synchronization.</li><li><strong>Short string equality:</strong> bounded loads compare short strings without an ordinary Java method call. Longer ranges still use memcmp; mixed encodings preserve their own comparison rules. The implementation must not read past the logical range just because a wide load would be convenient.</li><li><strong>Immediate array streams:</strong> supported Stream.of(array) pipelines with known lambda callbacks can become one C loop. Escaping streams, unknown callbacks, sorted, distinct and unsupported terminals keep the lazy implementation.</li></ul><p>For example, this shape is eligible for the stream work when the surrounding method satisfies the proof:</p><pre>long count = java.util.stream.Stream.of(names)<br>        .filter(name -&gt; name.length() &gt; 3)<br>        .count();</pre><p>Here names is a String[]. Eligibility is narrower than “streams allocate nothing.” Nor does seeing a loop in emitted C prove that the linked binary vectorizes it. The lowering audit checks generated code and linked instructions separately from elapsed-time measurements.</p><h3>Keep your program correct when removing work</h3><p>A declared List type does not prove an ArrayList layout. LocalReceiverTypes tracks allocation provenance, and unknown parameters or factory results retain guards. Analysis is restricted to methods that need it, and the compiler drops the temporary frame information after capturing the proof. Optimizing the translated program should not require keeping an unnecessary analysis graph alive throughout translation.</p><p>Direct calls also gain small C intrinsics for operations such as list access, string length, hashing and builder appends. They apply to calls whose target is resolved statically, with the ordinary implementation available off the fast path. Together with traversal lowering, this lets the C compiler see useful operations inside a loop instead of opaque calls at every step.</p><p>Another pass removes instance fields the closed program never reads. It checks native-source references and conservatively keeps runtime fields and unresolved cases. Removing a dead reference field can save both its storage and the object it would otherwise retain. This pass is disabled for JavaScript output and on-device debugging.</p><p>There is a semantic edge worth stating: the current <a href="https://github.com/codenameone/CodenameOne/blob/master/vm/ByteCodeTranslator/src/com/codename1/tools/translator/DeadFieldElimination.java">dead-field pass</a> documents that assigning to a removed field on a null receiver no longer produces the JVM’s NullPointerException. Its treatment of an unused store is therefore not a claim of identical behavior for every Java program. An application depending on that exception needs to account for this difference.</p><h3>Fit the collector to the cores you have</h3><p>The small-object allocator groups objects into pages by size, often called BiBOP, short for “big bag of pages.” A nonmoving collector cannot return a page while one live object still occupies it. Free slots inside a partial page can be reusable capacity without being releasable memory.</p><p>The experiments found 50 to 60 MB of free slots inside partial pages around one peak. Those slots were reused before new pages were allocated. Calling all of that a leak would send the investigation in the wrong direction.</p><p>On one core, the generational collector can stop application work and collect young objects. With spare cores, the concurrent collector overlaps tracing with the application. The concurrent path needs grace for allocations that appear after a snapshot. That can retain dead objects longer, and shortening that lifetime requires preserving everything the snapshot could not yet see.</p><p>We tried reclaiming dead large objects every single-core cycle. The first attempts were unsound. A growing output buffer could be allocated after the extent snapshot and remain live only in a thread’s local state. If the collector could not resolve that pointer, it could free a live buffer.</p><p>The fix registers those large objects from allocation and rebuilds the relevant snapshot at each thread’s pause in that mode. The concurrent collector keeps its different grace rules. Applying one mode’s shortcut to the other added locking to a hot path without providing a benefit.</p><p>One-core translation, five interleaved rounds Minimum elapsed Maximum RSS JDK 25 13.74 s 557 MB Four-byte header before reclamation change 10.29 s 608 MB With large-object reclamation change 9.84 s 503 MB</p><p><em>A separate Linux container experiment isolating the reclamation change: minimum elapsed and maximum RSS from five interleaved runs. Lower is better. Its four-byte-header control peaked at 608 MB; the header-layout experiment reported 605 MB in a different set of runs. The </em><a href="https://github.com/codenameone/CodenameOne/blob/60c4f310bddcfc51e5acc75c63a57c43c73430d3/vm/selfhost/experiments/REGISTRY.md#L6605-L6652"><em>reclamation experiment record</em></a><em> describes this later comparison. The opening charts show the current baseline.</em></p><p>We then reduced work in minor collections: skip an already-old page object before expensive conservative resolution, and consult the monitor table only for pages that have had monitors. The experiment record includes the unsuccessful alternatives too. Fewer object bytes help memory use. Avoiding thousands of unnecessary collector operations helps execution time too.</p><h3>What changes for your app?</h3><p>If your application creates large graphs of small objects, the smaller headers and field packing can reduce their cost without a source rewrite. Proven collection traversals and stream pipelines can avoid temporary objects and dispatch. These are compiler and runtime changes, so you get them by rebuilding with a release that includes this work.</p><p>The benefit depends on what your app spends time and memory doing. A form with a large model, an importer that walks collections, and a service that allocates objects continuously stress different parts of the runtime. The allocation result above is a reason to check a busy handler before assuming it will behave like the compiler benchmark.</p><p>For an existing Codename One app, rebuild and compare a real interaction on your target device: startup, a large list or model load, and a sustained operation that creates objects. For a backend, compare resident memory at rest and under representative traffic, plus throughput and latency. Keep the input and build settings the same between releases. The <a href="https://www.codenameone.com/developer-guide/backend/">backend guide</a> covers packaging a small service you can test this way.</p><p>There is more room to improve. Our measurements point toward shorter-lived garbage in the concurrent collector and less overhead in small native collection buffers. A stop-the-world strategy saves memory on one core, but forcing it everywhere can lose too much execution time. The next step is to reduce that retention while preserving useful concurrency. That is an optimization direction, not a promise that one collector will win on every application.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7de1f64f853d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javarevisited/honey-i-shrunk-java-7de1f64f853d">Honey, I Shrunk Java</a> was originally published in <a href="https://medium.com/javarevisited">Javarevisited</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Can You Tell the Difference Between These iOS Tab Bars?]]></title>
            <link>https://medium.com/javarevisited/can-you-tell-the-difference-between-these-ios-tab-bars-c4a63ec4ef2a?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/c4a63ec4ef2a</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Mon, 02 Nov 2026 14:09:12 GMT</pubDate>
            <atom:updated>2026-11-02T14:09:12.900Z</atom:updated>
            <content:encoded><![CDATA[<h4>Reconstructing iOS 27 tab motion from real touches, spring equations, color matrices and a side-by-side simulator comparison.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HXiSaHPU-O2EbADukZecvA.jpeg" /></figure><p>Watch how the two tab bars respond to the recorded taps, holds, drags and changes of direction. Can you tell which one is UIKit and which one is drawn by Codename One?</p><p>That is a more useful test than a still image. Your users feel the delay before a selection moves, the way the glass follows a finger and the settling motion after release. A tab bar can have the right colors and still feel wrong in your hand.</p><p><a href="https://www.codenameone.com/blog/ios-27-glass-you-can-test/">Last week’s iOS 27 theme</a> was close, but I still wasn’t happy with those interactions. Getting closer meant recording UIKit’s presentation layers during real touches, fitting the motion to spring equations and reconstructing the glass effects. Here is the result, followed by the measurements and formulas behind it.</p><h3>Watch the same gestures twice</h3><p><a href="/https/medium.com/blog/ios27-measured-glass/light.mp4">Download the light comparison video</a>.</p><p><em>UIKit is on top and Codename One below. These committed iOS 27 simulator recordings use the same touch sequence: six taps, a held press, a tap on the selected tab, a slow drag and a flick. Each gesture occupies a 4.2-second slot aligned on the touch.</em></p><p><a href="/https/medium.com/blog/ios27-measured-glass/dark-tabs.mp4">Download the dark comparison video</a>.</p><p><em>The dark appearance exposes the edge, vibrancy and refraction differences more clearly. UIKit is above Codename One in both simulator recordings.</em></p><p>The <a href="https://github.com/codenameone/CodenameOne/blob/master/scripts/record-ios-tabs-side-by-side.sh">recording script</a> and <a href="https://github.com/codenameone/CodenameOne/blob/master/docs/videos/README.md">video notes</a> make the provenance inspectable. The remaining differences are part of the comparison: the lifted lens, its rounded ends and the saturation still deserve work.</p><h3>Why your tab bar can look right and feel wrong</h3><p>Setting the selected tab in code did not produce the complete native interaction. UIKit performs that motion for genuine touches on a controller-managed bar. The probe therefore uses XCUITest taps, holds, and drags.</p><p>There was another complication: UIKit did not attach a simple CAAnimation containing the whole trajectory. It updated layer values frame by frame. The useful record was the presentation geometry at each display refresh: position, bounds, transform, opacity and the touch timestamps that explained them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JWodj0L2oocSC5mQLhavpA.png" /></figure><p>Capturing an image on every frame changes the timing enough to contaminate the measurement. The probe separates geometry logging from optional frame snapshots. Snapshot runs are useful for comparing the lens at the same motion state, not for fitting its timing. Likewise, lossless screenshots over known backdrops reveal the blur; compressed video smears the very edges we need to inspect.</p><p>Those constraints are documented in the <a href="https://github.com/codenameone/CodenameOne/tree/master/scripts/fidelity-app/ios-native-ref/motion-probe">native probe</a>. They explain why “looks close in a recording” was not enough.</p><h3>Make taps, holds and drags respond differently</h3><p>For a fixed target, a damped spring follows this equation:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*br58z7GuxAx_Kfo5ReazvA.png" /></figure><p>Here x is position, xₜ is the target, ω controls the natural frequency and ζ the damping. Dots denote derivatives with respect to time. A critically damped spring has zeta = 1; lower damping allows an overshoot. The implementation uses the measured constants rather than retuning the entire animation by eye:</p><p>Channel Model in the current source Selection travel omega = 2*pi/0.4, zeta = 0.85 Lens lift Critical damping, omega = 2*pi/0.25 Platter return Critical damping, omega = 2*pi/0.4 Whole-bar press growth omega = 17.6, zeta = 0.59 Lens following a drag omega = 32, zeta = 0.90</p><p>The travel stops once the remaining distance after the overshoot is below about 0.195 points. The drag and press models include a measured 20 ms touch lag. These small details are visible because your finger gives the animation a moving reference point.</p><p>The current <a href="https://github.com/codenameone/CodenameOne/blob/master/CodenameOne/src/com/codename1/ui/TabGlassMotion.java">TabGlassMotion</a> and <a href="https://github.com/codenameone/CodenameOne/blob/master/CodenameOne/src/com/codename1/ui/TabGlassGesture.java">TabGlassGesture</a> contain the formulas, retained tables and stopping rules. The regeneration tool checks the closed forms against captures and rewrites the channels that remain tabulated.</p><h3>Let users drag across tabs without selecting each one</h3><p>During a drag the lens follows the finger on its own spring, so it can lag behind. On release, UIKit selects the tab under the finger, not the one nearest the lagging lens. The lens then settles toward that tab from its current position and velocity.</p><p>Resetting velocity at that handoff creates a visible interruption. Snapping to the nearest lens position after a fast flick creates another discontinuity. A press on the already selected tab must also lift and pulse without inventing travel to another tab. A held press has to keep its lifted state until release rather than play a fixed-duration tap and finish underneath your finger.</p><p>The release adds a small wobble. Our model uses a common starting point for it even though the captures show slightly different onset times across gesture types. That approximation is one reason to keep the full gesture suite alongside the short demo.</p><h3>Keep icons legible as the backdrop changes</h3><p>A vibrant tab icon is not simply painted with an alpha color. Its result depends on the glass behind it. The probe reads UIKit’s color matrices across tint sweeps, and VibrancyMatrix reconstructs the transform.</p><p>For a non-gray dark tint vector t, the measured form is:</p><pre>e = 0.5 * min(t) / max(t)<br>M = e*I + (0.5 - e) * (ones * transpose(t)) / dot(t,t)<br>offset = t<br>output = M * backdrop + offset</pre><p>I is the identity matrix and ones is a column of three ones. Gray tints have their own formula, so this expression is not a universal branch to paste over the implementation. Light appearance uses a different form. The <a href="https://github.com/codenameone/CodenameOne/blob/master/CodenameOne/src/com/codename1/ui/plaf/VibrancyMatrix.java">vibrancy source</a> and recorded fixture hold those cases together.</p><p>The selection platter also transforms the bar’s existing glass. Graphics.colorMatrixRegion applies that operation within a shape and optional mask. The lifted lens needs a separate operation: refraction around a circular bevel, color dispersion, an outline, rim light and shadow. glassLensRegion supplies that rendering path.</p><p>This is why adjusting the opacity of a rounded rectangle could only take us so far. The effect changes the content underneath it.</p><h3>Keep the glass animation on the GPU</h3><p>A more accurate motion model is wasted if the renderer misses the frames that distinguish it. The iOS bar glass now runs on the GPU instead of passing through a CPU blur with GPU synchronization around it. Mutable images reuse their stencil texture. The tab animation avoids asking for a second whole-component repaint, and the lens scratch texture is released after use.</p><p>The portable color-matrix operation reaches the JavaScript port too. The browser lens still lacks the full optics implementation. A shared API does not mean every rendering backend implements every effect today.</p><h3>Choose the appearance, then test your screen</h3><p>In the Settings app’s <strong>Build Hints</strong>, select:</p><pre>ios.themeMode=modern<br>ios.themeGeneration=27</pre><p>For the cloud toolchain, ios.xcode_version=27 is a separate setting. In codenameone_settings.properties, prefix build-hint keys with codename1.arg.. A local Xcode build uses the Xcode installation selected on your Mac.</p><p>Codename One draws these controls itself. The theme and its motion ship with the app, so adopting this appearance remains your decision. That control also gives us the responsibility to measure the behavior we reproduce.</p><p>Try the animation over a photograph, a scrolling list and a plain dark background. Hold it. Drag past the middle and release. The <a href="https://github.com/codenameone/CodenameOne/pull/5906">implementation in PR #5906</a> and the fixtures behind it make those interactions repeatable instead of leaving fidelity to memory.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c4a63ec4ef2a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javarevisited/can-you-tell-the-difference-between-these-ios-tab-bars-c4a63ec4ef2a">Can You Tell the Difference Between These iOS Tab Bars?</a> was originally published in <a href="https://medium.com/javarevisited">Javarevisited</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Do You Prefer Gradle?]]></title>
            <link>https://medium.com/javarevisited/do-you-prefer-gradle-ccdecd997079?source=rss-a8cc878ccaaa------2</link>
            <guid isPermaLink="false">https://medium.com/p/ccdecd997079</guid>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Shai Almog]]></dc:creator>
            <pubDate>Mon, 02 Nov 2026 14:08:39 GMT</pubDate>
            <atom:updated>2026-11-02T14:08:39.698Z</atom:updated>
            <content:encoded><![CDATA[<h4>Optional Gradle projects remove empty platform modules, support standalone backends, and use the same build engine as Maven.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*O0__xrM34s96Yjpty3ulRA.jpeg" /></figure><p>You can now choose Gradle for a Codename One project. Start with fewer files, add platform directories when you need native code, or create a standalone backend without a mobile app attached. A smaller project is easier to browse and leaves less scaffolding competing for space in an LLM’s context.</p><p>I like Maven XML. I know it is verbose. I also spent enough time at Sun Microsystems staring at deeply scripted, heavily patched make files to develop a lasting suspicion of clever build systems.</p><p>Someone starts with a reasonable shortcut. Someone else needs one more condition. A few years later the build has its own undocumented programming language and nobody wants to touch it. I would rather a build system be strict and limited. Give people enough power and, with the best intentions, they build a Frankenstein build.</p><p>That is a personal preference. Plenty of developers I respect love Gradle, or at least appreciate what it lets them do. This week we added <a href="https://github.com/codenameone/CodenameOne/pull/5922">optional Gradle support</a> because it lets us offer something useful: a smaller Codename One project, including a standalone backend with no client scaffolding.</p><h3>Start with fewer files</h3><p>The Maven app layout has a root POM and platform modules ready for native code. That makes the structure explicit, but a new app often has no code in most of those places.</p><p>A Gradle app starts with the sources and configuration it needs. Native directories appear when you generate native interfaces. A backend subproject appears when you add a backend.</p><p>For a concrete example, we converted the checked-in HelloCodenameOne sample with the current converter:</p><p>Project files Maven sample Converted Gradle sample Total files, including app sources and resources 358 305 Maven POMs 9 0 Gradle Kotlin configuration files 0 2</p><p>That removes <strong>53 files, about 15% of the tree</strong>. We counted the tracked sample before conversion and all files in the converter’s new directory afterward, excluding build output and caches. This is the existing demonstration app, with its resources and Kotlin source, rather than a comparison of two different Hello World programs.</p><p>The generated settings.gradle.kts is small enough to show in full, with its explanatory comments removed:</p><pre>pluginManagement {<br>    repositories {<br>        maven(&quot;https://repo.codenameone.com/maven2&quot;)<br>        gradlePluginPortal()<br>    }<br>}<br>plugins {<br>    id(&quot;com.codenameone&quot;) version &quot;7.0.274&quot;<br>}<br>rootProject.name = &quot;HelloCodenameOne&quot;</pre><p>The example selects release 7.0.274, which includes Gradle support. The app&#39;s own dependencies and Kotlin plugin go in build.gradle.kts; the framework&#39;s platform builders come from the Codename One plugin.</p><pre>MyApp/<br>  settings.gradle.kts<br>  gradle.properties<br>  gradlew<br>  gradle/wrapper/<br>  codenameone_settings.properties<br>  icon.png<br>  src/main/java/<br>  src/main/css/theme.css<br>  src/main/resources/</pre><p>Add build.gradle.kts when you have dependencies or build configuration of your own. Kotlin source belongs in src/main/kotlin when the project applies the Kotlin plugin.</p><p>That smaller tree helps a human understand an unfamiliar project. It can also reduce the irrelevant files an agent sees when building its context. The file reduction is measurable; the context saved depends on which files your agent reads.</p><h3>Keep the same builds when you switch</h3><p>We did not implement a second set of platform builders. The Gradle plugin calls the shared engine used by Maven. That includes upload staging, local builders, CSS compilation and bytecode compliance checks.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/871/1*4AtsBPMqlONi8rTjisvMZg.png" /></figure><p>From a generated Gradle app:</p><pre>./gradlew run<br>./gradlew cn1Test<br>./gradlew buildJavascriptLocal<br>./gradlew buildIosXcodeProject -Popen=false</pre><p>run starts the simulator. cn1Test uses the simulator test runner. The last two commands build the JavaScript port locally and generate an Xcode project. Tasks that submit cloud builds are available too, with the same service requirements as Maven.</p><p>There are limits. Running Gradle requires JDK 17 or newer and Gradle 8.5 or newer; the generated wrapper supplies Gradle. These projects target Java 17. They do not support legacy .cn1lib files or the old resource Designer task. The OpenAPI, gRPC and GraphQL generators and on-device debugging helpers still have Maven goals without equivalent Gradle tasks. The <a href="https://www.codenameone.com/developer-guide/gradle-project-workflow/">workflow chapter</a> lists the supported surface.</p><h3>Build a standalone Java backend</h3><p>In the <a href="https://www.codenameone.com/initializr/">Initializr</a>, choose <strong>Gradle</strong> as the build tool and <strong>Backend only</strong> as the project type. The server lives at the root, with its Java source and application properties. There is no client theme, simulator module or collection of empty native directories.</p><p>For a generated project named MyService:</p><pre>CN1_PROFILE=dev ./gradlew runBackend</pre><p>That runs the development server on the JVM. Stop it before changing the backend code; this is a restart workflow, not hot reload. To build and run the native executable:</p><pre>./gradlew backendPackage<br>CN1_PROFILE=dev PORT=9000 ./build/MyService</pre><p>The development profile uses the generated in-memory SQLite configuration. Without it, the starter reads its production settings and expects DATABASE_URL. The executable&#39;s name follows the project name.</p><p>An existing Gradle app can gain a server with:</p><pre>./gradlew addBackend<br>CN1_PROFILE=dev ./gradlew :backend:runBackend<br>./gradlew :backend:backendPackage</pre><p>I hope people will try the backend on its own. It is still experimental, but a small independent service can reveal useful constraints without requiring a mobile application first. The <a href="https://www.codenameone.com/blog/java-server-work-before-startup/">weekly overview</a> shows the Spring-style programming model it now supports.</p><h3>Try Gradle alongside your Maven project</h3><p>The converter leaves the original project in place and writes a sibling project. Replace VERSION with the Codename One plugin release containing this feature:</p><pre>mvn com.codenameone:codenameone-maven-plugin:VERSION:convert-to-gradle \<br>  -Dcn1.sourceProject=/path/to/MyApp \<br>  -Dcn1.outputDir=/path/to/MyApp-gradle</pre><p>The destination must be empty or absent. The conversion carries sources, resources, CSS and platform implementations into the new layout, and raises the application’s bytecode target to Java 17. A customized backend comes across; the untouched backend skeleton is optional. Legacy .cn1lib dependencies stop conversion with their names rather than silently disappearing.</p><p>Open the converted project and run its tests before switching the team’s build. Your old checkout remains available for comparison.</p><h3>Find the API for the code you are writing</h3><p>Last week we split the Developer Guide into chapters. Before that, we rebuilt the Javadocs. A backend-only project made the next problem obvious: a client API page is the wrong answer when you are writing a server.</p><p><a href="https://github.com/codenameone/CodenameOne/pull/5918">PR #5918</a> separates the <a href="https://www.codenameone.com/javadoc/">client API</a> from the <a href="https://www.codenameone.com/backend/javadoc/">backend API</a>. The references label their audience and link shared classes to their counterpart. Backend classes belong in the backend reference; a UI implementation package does not belong there just because it exists in the repository.</p><p>What you are writing Reference to start with A form, component or device integration <a href="https://www.codenameone.com/javadoc/">Client Javadocs</a> A controller, database service or scheduled job <a href="https://www.codenameone.com/backend/javadoc/">Backend Javadocs</a> Shared models and mapping code The shared class page, with its counterpart link</p><p>The distinction helps readers and code-generation tools for the same reason: it reduces the chance of choosing an API that exists in the wrong runtime. Both references are checked with doclint, so malformed documentation has a build-time consequence too.</p><h3>Try Gradle 9 for your Android build</h3><p>The optional Gradle project layout and the Gradle version used inside a generated Android project are different choices. You can keep your Codename One app on Maven and still test the newer Android builder.</p><p>In Settings, under <strong>Build Hints</strong>:</p><pre>android.gradleVersion=9</pre><p>Or in codenameone_settings.properties:</p><pre>codename1.arg.android.gradleVersion=9</pre><p>The current builder pairs Gradle 9.8.0 with Android Gradle Plugin 9.4.1. It adjusts the generated project for removed DSL elements and AGP’s built-in Kotlin support. This selects the experimental Gradle 9 / Android Gradle Plugin 9 path from <a href="https://github.com/codenameone/CodenameOne/pull/5919">PR #5919</a>. It is opt-in. Much of the surrounding tooling and library configuration still assumes Gradle 8. Test your actual native dependencies and generated Android project before adopting it; a successful empty app is not the same test.</p><p>I expect to keep using Maven for plenty of projects. Supporting Gradle gives teams another way into the same build machinery, and gives a small server a project tree that looks like a small server.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ccdecd997079" width="1" height="1" alt=""><hr><p><a href="https://medium.com/javarevisited/do-you-prefer-gradle-ccdecd997079">Do You Prefer Gradle?</a> was originally published in <a href="https://medium.com/javarevisited">Javarevisited</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>