jimble

Utilities#

jimble-util holds around 28,000 lines of tools brought over from the code this was ported from. This page is a map. Not an exhaustive list — what you use often, and where you get caught.

Note

Tidying this up into a public API is Phase 2. Both the package layout and the names are still as the code this was ported from left them, in places.

Data#

io.jimble.util.data.Data is a subclass of LinkedHashMap<String, Object>. Requests, SELECT results, and JSON are all handled with it (Request and response).

Data data = new Data();
data.putData(SITE_ID, 1L);
data.putData(SITE_NAME, "俺的まとめ");

Data site = data.getData("site");

assertEquals(1L, site.getLong("id"));
assertEquals("俺的まとめ", site.getString("name"));

DB values are read and written by column (Column). The column versions follow the table-name nesting.

putData(Column, value) Always builds the nesting
putDataTakeCare(Column, value) Matches the shape already there (flat if there is no nesting)
flattenTable(Table) / extractTableData(Table) Flatten / pull out. null if it is not there

Trap

The Optional variants — getStringOptional and the rest — write to the Data. When the key is missing they put an empty string first and then return it, so reading alone adds keys. Call one just before serialising to JSON, or inside a loop, and the output changes.

Trap

You cannot tell "missing" from "0". getString returns null, getInt returns 0, getBoolean returns false. When you need to tell them apart, use the Object versionsgetIntObject and friends — or isNull(key).

Note

toString() is a summary (keys and types only). That is to stop a single log line from spilling the whole contents, or from pulling an Async in. Call getJsonString() when you want the JSON.

JSON#

It is our own implementation (neither Jackson nor Gson).

Data nested = new Data();
nested.put("title", "記事タイトル");

Data data = new Data();
data.put("name", "俺的まとめ");
data.put("count", 3);
data.put("feed", nested);
data.put("tags", List.of("あ", "い"));

String json = data.getJsonString();
Data restored = Data.fromJsonString(json);

assertEquals("俺的まとめ", restored.getString("name"));
assertEquals(3, restored.getInt("count"));
assertEquals("記事タイトル", restored.getData("feed").getString("title"));
assertEquals(List.of("あ", "い"), restored.getStringList("tags"));
What you want How to write it
Data → string data.getJsonString() / getJsonString(true) (pretty)
String → Data Data.fromJsonString(json)
Any object Dson.encodes(obj) / Dson.decodes(json, Xxx.class)
Straight to a stream data.outputJsonString(outputStream)
Write it out piece by piece, without building it JsonHashWriter / JsonArrayWriter

Careful

The static Dson.encodes / decodes do nothing on failure but return null. If you need the reason, create a new Dson() and check isError() / getErrorException() after decode(...).

Type conversion#

Convertor.convert(conf, src, Target.class) is the way in. Bean ↔ Data ↔ Map ↔ List ↔ primitives all go down the same single path. data.convert(new MyBean()) is the same machinery underneath.

Trap

Do not reuse a Configration. Its depth counter and its record of circular references are rewritten as the conversion runs. Make one per conversion.

HTTP client#

A thin wrapper over the JDK's HttpClient, with a class per method.

HttpGetExecutor res = new HttpGetExecutor()
	.setUrl("https://example.com/api")
	.addHeader("Accept", "application/json")
	.setTimeout(5000)
	.execute();

if (res.isError) {
	Log.error(res.errorException, "could not fetch");
	return;
}

Data json = res.getContentJson();
  • POST is HttpPostExecutor. addBodyForm(name, value) / setBodyJson(data)
  • Add even one file and it becomes multipart (addBodyForm(name, file, contentType))
  • The proxy is setProxy(new HttpProxy(host, port, id, pass))
  • It does not throw. Check isError (the same style as the DB)

Careful

The default timeout is 30 seconds, and the same value goes to both connect and response. You cannot set them separately.

Trap

setIgnoreSslError(true) takes effect across the whole JVM. It raises a system property that turns certificate validation off, so every other connection in the same process stops validating too, and it never goes back.

CSV#

try (CsvReader reader = new CsvReader(new File("in.csv"))) {
	while (reader.next()) {
		String name = reader.getString("name");
	}
}

It reads a line at a time (it does not put the whole file in memory). For writing, CsvWriter#writeLine(Object...).

Careful

Leave the character encoding out and, when detection fails, you get Shift_JIS. If you know it is UTF-8, write new CsvReader(file, "UTF-8").

XML#

XmlParser.parse(file) turns it into a tree of XmlData (all of it goes in memory). To build one, XmlBuilder.build(xmlData).

Hashing and encryption#

What you want What to use
Passwords PasswordUtil.createHash / check (Sessions and safe defaults)
Signatures (tamper detection) Signer.sign / unsign (HMAC-SHA256)
Encryption Aead.encrypt / decrypt (AES-256-GCM)
Short IDs Hashids
Hash values Hash.md5 / sha256 / sipHash / xxHash64

Careful

CipherUtil (AES/CBC) takes its IV from the configuration, so it is fixed. The same plaintext always gives the same ciphertext, and tampering cannot be detected. It is kept for compatibility with the code this was ported from. Use Aead for anything you write new.

Trap

The default Hashids instance has an empty salt. The sequence can be guessed, so it cannot hide an ID you do not want seen. Build your own with a salt if you need that.

Key-value stored in the DB#

DBValue.set(db, "last_imported_at", "2026-09-07");
String value = DBValue.getString(db, "last_imported_at", "");

Trap

When there is no value, get writes the default in (you meant to read, and an INSERT goes out). Values are up to 250 characters, and there is no API to delete one. The cache is per process, so a set on another machine never reaches you.

The rest#

Area Classes
Strings StringUtil (replace, full-width/half-width, Base62, password generation) / IcuUtil (kana, full-width/half-width)
Date and time DateUtil (built on java.util.Date. getFrom / getTo mean "00:00:00 / 23:59:59 on that day")
URLs UrlUtil / UrlBuilder (domain extraction, punycode, encoding)
Numbers and parsing Parse.parseInt and friends (they do not throw on failure)
Regular expressions Patterns (email, URL, domain, phone)
Files FileUtil / IOUtil / FileCharDetecter (character encoding detection)
Threads VirtualThreadManager / ThreadManager (Execution model)
Measurement StopWatch

Note

Patterns' TLD list is a hard-coded string. New TLDs do not match.