nortti's microblog

J2ME in 2026

Basically all tutorials on J2ME I can find are

  1. ancient
  2. assuming you're using some kind of IDE
  3. working off of Sun's WTK
  4. some combination of the above
This is not great.

I have, fortunately however, managed to piece together enough information to be able to do so. I'll walk through building a simple J2ME app, using only free software that's still maintained (well, mostly) so that this is documented somewhere.

For tooling, we'll be using

OpenJDK 8
Still maintained for the time being. It's the last version of OpenJDK that supports code generation for the old versions of Java we're targeting.[citation needed]
ProGuard
We're not interested in "protecting" our application, but J2ME requires preverification and ProGuard appears to be the best choice here somehow.
MicroEmulator
Okay this one's not actually maintained anymore. But we're just using their stub libraries for the J2ME APIs.
bsdtar
Other ZIP-file creation utilities are available.
FFmpeg
Other image processing utilities are available.

$ mkdir -p ro/plesoianu lib out/ro/plesoianu out/META-INF
$ cd ro/plesoianu
ro/plesoianu$ wcurl https://felix.plesoianu.ro/mobile/pocket-gopher/PocketGopher.java
  % Total    % Received % Xferd  Average Speed  Time    Time    Time   Current
                                 Dload  Upload  Total   Spent   Left   Speed
100  22743 100  22743   0      0  28807      0                              0
ro/plesoianu$ cd ../../lib
lib$ wcurl https://repo1.maven.org/maven2/org/microemu/midpapi20/2.0.4/midpapi20-2.0.4.jar https://repo1.maven.org/maven2/org/microemu/cldcapi11/2.0.4/cldcapi11-2.0.4.jar
DL% UL%  Dled  Uled  Xfers  Live Total     Current  Left    Speed
100 --  87390     0     2     0                              449k      
lib$ cd ..
$ /usr/local/openjdk8/bin/javac -source 1.2 -target 1.2 -d out -bootclasspath lib/midpapi20-2.0.4.jar:lib/cldcapi11-2.0.4.jar ro/plesoianu/PocketGopher.java
warning: [options] source value 1.2 is obsolete and will be removed in a future release
warning: [options] target value 1.2 is obsolete and will be removed in a future release
warning: [options] To suppress warnings about obsolete options, use -Xlint:-options.
3 warnings
$ cd out
out$ wcurl https://felix.plesoianu.ro/mobile/pocket-gopher/home.txt
  % Total    % Received % Xferd  Average Speed  Time    Time    Time   Current
                                 Dload  Upload  Total   Spent   Left   Speed
100   1245 100   1245   0      0   3383      0                              0
out$ ffmpeg -loglevel error -f pbm_pipe -i - gdimp.png << EOT
> P1
> 32 32
> 0000000000000000000000000000000000000000000000000000000000000000000000
> 0000000000000000000000000000000000000000000000000000000000000001110000
> 0000000000000000000000001100000000000000000000000000000110000000000000
> 0000000010000000010000000000011000000001100000000100000000001010011000
> 0100000000110010000001101001111001110000001001111000110010010010011100
> 0000110001100010001001001001011000000111110000111010010110010010000000
> 0000000000111011110000001000000000000000000000111000000010000000000000
> 0000000011000000001000000000000000000000100000000000000000000000000000
> 0000000000000000000000111100000000000000000000000000001001111100000000
> 0000000000000000100000010000000000000000000000001000001100011110000000
> 0000000000111011100001101100000000000000000011100000011001100000000000
> 0000000011000001100000000000000000000000010000010000000000000000000000
> 0000000001000000000000000000000000000000010000000000000000000000000000
> 0001000000000000000000000000000000000000000000000000000000000000000000
> 00000000000000000000000000000000000000000000
> EOT
out$ unix2dos > META-INF/MANIFEST.MF << EOT
> Manifest-Version: 1.0
> MicroEdition-Configuration: CLDC-1.1
> MicroEdition-Profile: MIDP-2.0
> MIDlet-1: Pocket Gopher rebuilt, /gdimp.png, ro.plesoianu.PocketGopher
> MIDlet-Description: Rebuild of PocketGopher sources in 2026
> MIDlet-Icon: /gdimp.png
> MIDlet-Info-URL: http://microblog.ahti.space/nortti/2026-08-25-j2me-in-2026.html
> MIDlet-Name: Pocket Gopher rebuilt
> MIDlet-Vendor: Felix Pleșoianu, nortti
> MIDlet-Version: 1.0
> EOT
out$ tar -cf ../not-preverified.jar --format zip *
out$ cd ..
$ proguard -injars not-preverified.jar -outjars pocketgopher-rebuilt.jar -libraryjars lib/midpapi20-2.0.4.jar -libraryjars lib/cldcapi11-2.0.4.jar -dontshrink -dontoptimize -dontobfuscate -microedition
ProGuard, version 7.0.0
Reading program jar [not-preverified.jar] (filtered)
Reading library jar [lib/midpapi20-2.0.4.jar] (filtered)
Reading library jar [lib/cldcapi11-2.0.4.jar] (filtered)
Preparing output jar [pocketgopher-rebuilt.jar] (filtered)
  Copying resources from program jar [not-preverified.jar] (filtered)

Inshallah I'll write a proper writeup that exaplains what every bit here actually is for. For now, my sources were:

Creating deployable MIDlets with free software,
Pointed me at the proper FOSS software to look at and explained what ProGuard is actually needed for.
[EN] (Much) Faster Way to J2ME Development in 2026, 2026
Told me where to get the MicroEmulator stub libraries nowadays (the answer is Maven Central).
sdk/compile.sh in the discord-j2me project
Showed the actual commands to run if you are not using an IDE.

#wikifinds

Robberies are not common in Antarctica.[3]

(src)

#wikifinds

Between 600,000 and 200,000 years ago,[citation needed] "Heidelberg Man" died at nearby Mauer.

(src)

Character encoding is hard I guess

[nortti@heinä ephemeral.EmlLr9ILvM]$ openssl req -new -x509 -newkey rsa:2048 -subj '/CN=heinän PK-avain/' -keyout PK.key -out PK.crt -days $((20*365)) -noenc -sha256
..+...+......+.........+............+..+.+..+..........+..+++++++++++++++++++++++++++++++++++++++*...+........+.......+............+.....+...+++++++++++++++++++++++++++++++++++++++*.+....+.........+......+...+..+..........+.....+....+............+..+...+...+.+...+..+.............+.....+...+....+........+.+.....+.++++++
...+..+.........+.+...+..+.+...+............+............+++++++++++++++++++++++++++++++++++++++*.............+.+...+...+..+++++++++++++++++++++++++++++++++++++++*....+...+......+.....+....+...+..............+..........+.....+.+.....+.........+.+..+...+.........+.............+.....+..........+..+......+...+.+...............+...+.....+......+.+........+......+.......+.......................+.......+..+..........+..+.........+.+........+.+..+....+.....+.+...+.....+..........+.....+.......+..+...+.........+................+..+...+.......+.....+...+...+.+......+...+......+......+..+.............+.........+.....+......+.........+....+.........+...+............+.....+...+...+.......+...+.....+....+...........+.......+............+..+.........+....+...+.....+...+....+...............+...........+.........+...+....+.....+.+...+............+..+.+..+...+.+........+...+....+......+..............+..................+...+............+......+..........+............+..+.+...........+.+.........+.........+.....+..........+...........+...+.+.....................+...+.....+.+.....+......+.+..+...+...+....+...+.....+.........+....+.....+......+..................+.+..+...+.......+..+.......+..............+.......+.....+.......+...........+.+.........+..+...+............+.......+............+......+..+...+...+....+............+.........+......++++++
-----
[nortti@heinä ephemeral.EmlLr9ILvM]$ openssl x509 -in PK.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            3f:1d:b4:47:d8:22:73:3d:b2:89:e1:9b:e6:aa:4d:98:b1:b1:f9:80
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=heinän PK-avain
        Validity
            Not Before: Aug 10 18:56:22 2026 GMT
            Not After : Aug  5 18:56:22 2046 GMT
        Subject: CN=heinän PK-avain
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b9:e5:f2:0e:92:2a:83:61:37:b4:d7:27:53:b0:
                    16:e5:97:cb:66:53:5b:23:9e:75:58:5f:d7:82:60:
                    96:b3:51:76:c1:81:f8:f4:52:28:24:a5:2f:05:64:
                    55:f4:61:b6:c9:86:fe:46:a6:cd:97:f6:32:f7:fb:
                    5c:e3:44:c1:8d:82:75:bf:35:26:ac:85:19:05:0a:
                    09:b8:81:0f:0b:96:85:ff:94:bb:e0:ae:46:ec:82:
                    d5:e8:e9:5b:b9:54:c4:fe:ac:2a:b3:f7:c2:b9:8d:
                    04:6b:9d:22:f8:0b:b2:49:af:ce:95:87:38:ea:5e:
                    74:b8:1b:49:6e:33:39:44:90:e4:ee:f7:dd:80:ac:
                    40:c0:b4:22:09:7a:99:80:f0:04:06:78:7f:09:a9:
                    32:44:14:64:ab:3c:3a:44:25:a3:16:1a:44:0d:4c:
                    cb:85:b7:e1:b0:88:43:7e:8c:13:94:43:b7:fb:01:
                    1c:95:54:14:83:ef:73:fc:06:3c:b1:76:75:26:e6:
                    a6:03:72:43:47:94:24:6e:6c:f7:c0:8b:94:23:15:
                    95:0e:a4:dc:fa:71:7e:4c:36:a4:aa:2e:e4:64:69:
                    5a:75:b4:ab:4a:e7:4e:87:97:5a:a2:fd:df:11:2b:
                    96:4b:87:c1:37:0a:26:27:23:63:c3:d1:98:b3:aa:
                    5b:27
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                40:CC:76:B4:AD:E4:41:64:30:E0:A7:4D:60:3C:32:A1:7B:5F:31:5B
            X509v3 Authority Key Identifier: 
                40:CC:76:B4:AD:E4:41:64:30:E0:A7:4D:60:3C:32:A1:7B:5F:31:5B
            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        40:ef:47:52:b9:fc:2e:97:d1:d4:93:03:8a:08:46:e5:51:9f:
        65:f1:be:a7:f2:07:8c:c1:54:30:a8:fa:66:5a:35:31:d1:b3:
        12:b9:10:e1:55:2e:4e:19:23:c4:28:c1:f4:5a:29:df:be:94:
        99:67:9c:de:02:7b:15:95:79:6d:c4:67:fb:49:cf:0c:54:68:
        e4:a6:d7:07:05:ea:3a:1c:16:81:3b:d8:6b:c8:bb:f7:ad:41:
        af:a4:3a:0e:ca:18:6c:69:b3:91:22:87:e6:92:1c:3a:6e:50:
        e1:d6:7f:16:3a:55:e8:fb:84:2d:de:f3:95:40:28:51:38:5a:
        c2:75:87:e7:83:ae:39:96:65:83:44:f0:b4:bd:b4:9e:8e:e6:
        0b:5e:b9:16:ae:0d:52:29:3c:9f:02:bb:56:ed:3c:75:eb:18:
        55:13:bf:2d:5d:a7:82:ea:18:6d:a7:c8:fe:dd:72:d1:14:7d:
        79:16:e9:e9:26:2b:3d:89:fb:e5:4d:eb:f0:77:b6:07:c6:74:
        23:b7:33:22:26:3b:80:37:39:7c:84:5a:a0:9a:95:59:a7:5b:
        ef:6e:e9:ae:7e:9f:33:06:ec:04:f0:c1:47:5f:f8:e2:1b:0d:
        66:3b:0d:3d:20:63:7a:e5:49:d4:79:e6:46:6e:66:68:d1:98:
        73:62:70:92
[nortti@heinä ephemeral.EmlLr9ILvM]$ openssl asn1parse -in PK.crt | rg hein
   61:d=5  hl=2 l=  18 prim: UTF8STRING        :heinän PK-avain
  124:d=5  hl=2 l=  18 prim: UTF8STRING        :heinän PK-avain
[nortti@heinä ephemeral.EmlLr9ILvM]$ tail -n +2 PK.crt | head -n -1 | base64 -d | rg -ao 'hein.*vain' | hexdump -C
00000000  68 65 69 6e c3 83 c2 a4  6e 20 50 4b 2d 61 76 61  |hein....n PK-ava|
00000010  69 6e 30 1e 17 0d 32 36  30 38 31 30 31 38 35 36  |in0...2608101856|
00000020  32 32 5a 17 0d 34 36 30  38 30 35 31 38 35 36 32  |22Z..46080518562|
00000030  32 5a 30 1d 31 1b 30 19  06 03 55 04 03 0c 12 68  |2Z0.1.0...U....h|
00000040  65 69 6e c3 83 c2 a4 6e  20 50 4b 2d 61 76 61 69  |ein....n PK-avai|
00000050  6e 0a                                             |n.|
00000052

kato

When workin on the terminal, I've ended up preferring to take advantage of the terminal scrollback where reasonable. As such, I've moved to using cat(1) instead of less(1) for when I want to read a text file.

There are some downsides to using cat(1), though. First of all, you're allowing the author of a file to send arbitrary instructions to your terminal. This turns out to be suboptimal for security. Secondly, there is no way to tell where one file ends and another starts. This is what you want when you are using it to concatenate several files together, but it is annoying if you want to quickly look through a directory full of many small files.

I wrote a small tool called kato to solve these problems for me.

#wikifinds

Dietrich von Bern and Theoderic the Great were usually treated as the same figure throughout the Middle Ages.[1][2] However, the lives of Dietrich von Bern and Theodoric the Great have several important differences. Whereas Theodoric the Great conquered Italy as an invader, Dietrich von Bern is portrayed as exiled from his rightful kingdom in Italy. Also, Dietrich is portrayed as a contemporary of Etzel (Attila the Hun, died 453) and his uncle is the semi-legendary Gothic king Ermenrich (Ermanaric, died 370s).[3] Dietrich is associated with Verona (the Bern of his name) rather than the capital of the historical Theodoric, Ravenna; the connection to Verona is attested since at least the eleventh century in Latin chronicles, beginning with the Annals of Quedlinburg.[4]

Dietrich has a number of mythological features: In the early eleventh-century Waldere he is an enemy of giants,[5] and in later Middle High German texts he also fights against dwarfs and wild men.[6] Even more notable is the fact that multiple texts record Dietrich breathing fire.[7]

(src)

Alkulukuja Paskova Karhu

https://alpha61.com/primenumbershittingbear/ (via)

Now on Fediverse

Ever since Cohost closed down I've been thinking on-off that I should maybe set up presence on the Fediverse. Mostly off, though, as I can generally just follow the ppl I know feeds without having an account myself, and if need be send replies through other channels. Recently, however, I've had a couple times where the only method of contact I'd have was fedi or something even worse.

So, partly for that reason, and partly just to get some more experience sysadmining OpenBSD, I've set up a snac2 instance at fedi.nortti.org

Feel free to send a follow and message me there. I probably won't post much, but it is a way that I intend to be reachable.

#wikifinds

The studio was intent on promoting its "all-talking" stars, and silent-film personality Rin Tin Tin obviously could not speak.

(src)

Haunt'd

I've rebuilt the site using Haunt. If everything goes well, you should see (essentially) no changes.

This will also mean that, hopefully, I'll start posting more often here again.

Next