Repository navigation
Commit 65ae4d8
Backend: Spring-style beans, transactions, scheduling, metrics and MCP, resolved at build time (#5908)
* Backend: Spring-style beans, transactions, scheduling, metrics and MCP, resolved at build time
The backend gains Spring's programming model under com.codename1.backend.annotations
-- @Service/@Component/@Repository, @Autowired, @Value, @ConfigurationProperties,
@Bean, scopes, profiles and conditions, @Transactional, @Async, @Scheduled,
@ManagedResource/@Timed/@Counted and @McpTool -- with every decision made by the
build: a generated BackendWiring constructs and injects the beans with plain code,
and aspects are woven into the annotated methods. No container, scan, proxy or
reflection at run time.
Runtime: thread-bound transactions that the pool, daos and sessions join, a cron
and fixed-rate scheduler with an optional database lock, task executors including
fd-less virtual-thread tasks with a per-host wake pipe, HTTP sessions (memory or
JDBC store), OTLP metrics and management endpoints, and an MCP endpoint with
development tools.
Also fixes the sticky virtual-thread yield reason (yieldNow from a handler hung),
a handler taking HttpServer.Request being refused, and adds the backend and
full-stack references to the generated agent skill.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: fix review findings in sessions, tasks, MCP, metrics and wiring
- Sessions: the session cookie goes on a copy of the handler's Response,
never into it (it may be a shared constant); cn1.session.secure refuses
anything but auto/true/false.
- TaskExecutor: virtual submissions are refused after shutdown and counted
active before the hand-off; @Async accessors re-fetch a shut-down executor.
- MCP: only loopback or listed origins pass (the Host match let DNS
rebinding through); byte/short tool arguments are range-checked;
backend_call uses https against a TLS server.
- Request metrics are recorded in a finally, so failures count and the
route label is cleared.
- Factory beans inherit their configuration class's @Profile and
@ConditionalOnProperty; request-scoped beans' destroyMethod runs.
- Scheduler lock: an INSERT failure with no existing row is rethrown.
- OTLP histogram bucket_counts stays fixed64 (per metrics.proto), now held
by a test decoding with the generated opentelemetry-proto classes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: per-server sessions and registries, bean lifecycle fixes; split the guide
Review fixes:
- Sessions, MCP tools and managed beans are per server (Backend.getSessions,
Environment.registerTool/registerManaged, Builder.mcpTool); two servers in
one process no longer share cookies, sessions or tools.
- @SessionScope beans are kept by the server for the session's lifetime and
destroyed on invalidate, expiry and stop; a negative session timeout fails.
- Transactions: no process-wide default pool; a NESTED method before the
first statement sets its savepoint after BEGIN. setRollbackOnly in the
method that began the transaction rolls back without throwing.
- Backend.stop runs destroy callbacks once; a failed start destroys built
beans; management routes precede application handlers; session-store
failures are logged as 500s; metrics shutdown is bounded by its timeout.
- Weaving keeps synchronized on the body, so a synchronized @Async method
holds its monitor where it runs.
- Injection points and lifecycle methods inherited from base classes are
wired; @ConditionalOnMissingBean matches the bean's exposed types and
takes explicit ones.
Docs: the backend chapter is split into nine chapters (web, beans, data and
transactions, sessions, scheduling, observability, MCP, operations) with
diagrams and compiled samples.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: per-server executors, bounded shutdown, lifecycle and DST fixes
- Executors belong to the server that opened them: request, task,
scheduler and start-up threads carry it, stopping one server no longer
shuts down another's, and generated @Async code looks the executor up
per call. At the shutdown deadline queued tasks are dropped rather than
run against destroyed beans, and running ones are interrupted; a virtual
task a host cannot run falls back to its own executor.
- Every server applies cn1.session.* (handler-only ones sent a TLS
session cookie without Secure); a failing request still stores its
session so its session beans are kept or destroyed.
- Request beans are destroyed while their request is still current;
@PreDestroy runs subclass before superclass; factory beans run
inherited lifecycle methods.
- Health reports STARTING until the start-up hook returns.
- A cron time inside a DST gap is skipped instead of firing an hour late.
- The metrics exporter can be reopened; histograms copy and validate
their bounds and labels.
- A managed resource must be a singleton and cannot overload operations.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: drain virtual tasks at shutdown, harden JDBC sessions and wiring
- A stopping host keeps resuming its background virtual threads through the
drain window instead of exiting with them yielded; stop() waits for that
(except on the host running a handler that called stop), and a task that
overruns is freed and reported.
- JDBC sessions: a stale copy of an invalidated session is not written back;
the stored last use is refreshed at min(1 min, timeout/4) and expiry
allows that much grace; session-scoped beans are shared per session id
across copies, created under one lock.
- NESTED setRollbackOnly rolls back only its savepoint.
- Built-in server gauges sum over running servers and drop stopped ones.
- Generated wiring clears every bean field before a restart; factory beans
step aside with a @ConditionalOnMissingBean configuration; scoped-bean
stand-ins forward methods inherited from library classes; a
managed-resource-only module gets an application.
- Runtime cron parsing refuses dates no allowed month has; MCP long
arguments are range-checked; unconfirmed backend_sql runs in an enforced
read-only transaction, one statement, no value-setting pragmas.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: finish abandoned tasks, order request beans before the session save
- A virtual task freed at shutdown now releases its executor's active count
and fails its Future, instead of leaving both hanging.
- Request-scoped beans are destroyed before the session is stored, so a
@PreDestroy that changes or starts the session is saved and its cookie sent.
- Loading a session refreshes its shared beans' last use, so a purge cannot
destroy beans a long request is still using.
- Scheduler.trigger refuses after stop and restores the job if the executor
rejects it.
- backend_sql runs only allow-listed read pragmas unconfirmed (either
spelling), and closes a connection whose rollback failed.
- The build warns about public methods a class-level @Transactional or
@Async class inherits, which the annotation does not cover (as in Spring).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: roll back failed statements as Spring does; per-server logs and metrics
- Database failures are DataAccessException (an IOException), and the
default @Transactional rule rolls back for it beside RuntimeException and
Error: in Spring a failed statement is an unchecked DataAccessException and
rolls back. Other checked exceptions still commit, as Spring's rule says.
- JDBC sessions save by optimistic merge: a versioned row, only this
request's changed attributes applied, last use only moving forward; the
purge no longer overflows PostgreSQL INTEGER for long timeouts.
- Request logs are per server; request and job metrics are recorded only by
servers that measure.
- Duplicate @McpTool and @ManagedResource names are refused at build time
(and at start-up for conditional beans).
- Cron searches a full 400-year cycle; fixed-rate catch-up is arithmetic.
- Managed operations answer 400 for bad JSON or rejected arguments and 404
only for an unknown bean or operation.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: per-server tracers and gauges, dependency-ordered destruction
- Each server keeps its own tracer until it stops: a later server's start
no longer retires a live one's, request spans use the tracer of the server
they reached and child spans their parent's, and the global slot passes to
another live server's tracer when its own stops.
- Managed-resource gauges are sources of a shared gauge, added when their
server starts and removed when it stops.
- Lazy singletons are destroyed in dependency order with the eager ones;
request and session beans dependents first.
- An @Async call dropped at the shutdown deadline fails its Future.
- Counters export through OTLP as_int and print exactly in Prometheus.
- Jobs whose classes share a simple name take qualified names.
- Virtual tasks run only on their own server's hosts.
- MCP enum arguments match constant names; Prometheus name collisions are
refused when the second instrument is created.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: safer start-up failure, init order, session replacement
- Any failure after the listener binds, before a Backend owns it, stops the
listener, and gauges a failed start added are removed.
- @PostConstruct follows every injection, fields and setters included.
- A connection whose ROLLBACK fails in Database.transaction is closed, so the
pool discards it instead of lending it to a borrower that would hang.
- The generated application binds its scheduler to the server before
starting it: job metrics are recorded when the server measures, and job
spans go to the server's own tracer.
- After invalidate(), getSession(false) answers null and getSession(true)
starts a new session in the same request; both are finished at its end.
- @Async on a @RequestScope bean, and @Async with @Scheduled, are build errors.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: clean up after Errors at start-up; tighten sessions, MCP, metrics
- Start-up clean-up runs through finally blocks, so an Error from a bean
(a failed static initializer, a missing class) closes the pool, stops the
executors and listener, destroys what was built and rolls the tracer back.
- Every session a request invalidates is finished, not only the last; a new
session is not stored when no response can carry its cookie; the session
cookie name must be an HTTP token.
- @McpTool with @Async, and duplicate or empty tool/operation parameter
names, are build errors; float arguments are range-checked.
- A @WebSocketMapping path with a percent escape is a build error.
- A refused shared gauge leaves no entry behind; counters refuse to overflow.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: guard post-start hooks, protect in-use session beans, scope rules
- Every hook after the Backend exists (management, MCP attach, the
application's started()) runs under one guard that stops the server if
any fails.
- Session beans are not expired while a request is using their session:
each server counts requests per session id until the request ends.
- Histograms keep a null first label apart from an empty one.
- A @SessionScope bean cannot inject HttpSession (a per-request copy with
the database store), and a websocket endpoint cannot inject request- or
session-scoped beans; both are build errors.
- @Scheduled, @McpTool and managed methods inherited from a superclass are
registered.
- The request-metrics switch is an AtomicBoolean, visible to running workers.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: test that expiry spares session beans a request is using
The purge is package-private so a test can run it at a chosen time instead
of waiting for the next once-a-minute lookup; the test fails with the in-use
check removed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: database-time locks, atomic session rotation, per-server callbacks
- Scheduler lock leases use the database server's clock (PostgreSQL,
MySQL), so a replica running ahead cannot take a held lock.
- A JDBC session rotation is one transaction that locks the old row and is
refused when it is gone, so a stale copy cannot undo a logout.
- Session in-use counts follow the HttpSession object, covering replaced and
rotated sessions; a bare HttpServer refuses sessions it could never store.
- WebSocket callbacks carry their own server's executors.
- as_int encodes as sfixed64, so a negative up-down counter exports.
- Virtual-task submission rechecks, under the inbox lock, that shutdown has
not already drained the host.
- Unknown cron zones are refused; an Error from a lifecycle hook no longer
abandons shutdown; a second metrics exporter with another identity is
refused, since metrics are per process.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: spare in-use sessions in the store, finish sessions on Errors
- The store purge (memory and JDBC) skips sessions a running request uses.
- A handler that throws an Error still has its invalidated and changed
sessions stored.
- @Async on a @SessionScope bean is a build error, as on a request bean.
- Task queue-depth points are summed per executor name; histogram label
keys that collide in Prometheus, or are "le", are refused.
- The management endpoint's backend and the request-log switch are
volatile, so running workers see them.
- Managed-resource names must be one URL segment; duplicate programmatic
job names are refused; the stdio bridge answers under the top-level id.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: contain tracer Errors, honour lost rotations and leases, check tokens
- Every hook into the tracer now catches Throwable: an AssertionError or
LinkageError out of a tracer escaped request start-up, and since the
tracer stays installed, every later request failed the same way.
- A database session rotation that finds its old row gone (another request
of the client rotated it first) is recorded on the session, and the
request sends no Set-Cookie: the id it would announce has no row.
- A scheduler claim writes a lease unique to that run, and release matches
it, so a run that outlived lockAtMostFor cannot free the claim a later
run (of any job sharing the lock name) took after it expired.
- Prometheus label names are folded without the colon a metric name may
carry; histogram label collisions use the same fold.
- An executor shut down from one of its own tasks (an @Async method that
stops the server) no longer waits out the timeout for that task, nor
interrupts it.
- A double of exactly -2^63 is refused as a long argument, like 2^63.
- Config.getHeaderSecret refuses a bearer token no request could carry, for
the MCP and management tokens as well as the OTLP relay's.
- Three doc comments the /// conversion had merged into their neighbours
are back on their own members, or gone where the member no longer exists.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: spare in-use sessions on lookup, check the whole bean graph off-request
- A lookup no longer expires a session another request is still using: the
database store writes the last use when a request ends, so a long request
made the stored time look expired and the next request deleted the row and
the beans under the first. The purge already spared these ids.
- A class a @Bean method builds keeps its @Scheduled jobs, @McpTool methods
and @ManagedResource surface; only its lifecycle methods were collected.
- Websocket endpoints, beans with @Scheduled jobs and @ManagedResource beans
are checked through their whole dependency graph for request- and
session-scoped beans, and the error names the path. All three run with no
request current; only an endpoint's direct injections were checked.
- A prototype controller, or a prototype with MCP tools or managed
attributes, is built once for the router: the reference was evaluated in
the null test and again for the router, building a second instance.
- A @Bean method that returns null stops the start, naming the method,
instead of handing null to what injects it.
- MCP: an explicit "id": null is a request and is answered; only an absent
id is a notification.
- A number argument given as "NaN" or "Infinity" is refused.
- OTLP/JSON metrics write non-finite doubles as the protobuf JSON strings
("Infinity", "-Infinity", "NaN") rather than null.
- Websocket callbacks report to their own server's tracer, through a
per-thread owner the callbacks bind, rather than to whichever server
installed its tracer last.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: undo a stale memory rotation, finish every session, bound the bridge
- The memory store shares one HttpSession between concurrent requests, so a
second request rotating it after the first had rotated and answered would
delete the id the first response carries, and signed the client out if
that response arrived last. A request now records the id it found each
session under; a rotation whose replaced id is not that one is undone and
not announced, as the database store's conditional move already settled.
- A request that ends several sessions finishes each one on its own: a store
failure on one no longer skips the deletes, saves and bean clean-up of the
others.
- The stdio bridge sets connect and read timeouts (CN1_MCP_TIMEOUT_MS, two
minutes by default), so a backend that accepts and stalls is reported
under the request's id instead of blocking every later message.
- Gauge callbacks and the metrics export contain any Throwable: an Error from
an application's gauge used to end the exporter's only thread.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: forget found session ids, run notifications, check executor kinds
- The ids a request found its sessions under are cleared with its other
session state, when it ends and when a pooled request is recycled: a
keep-alive connection's request otherwise held every session it had
served, attributes and all.
- An MCP notification runs its method; only the answer is withheld. An
id-less tools/call used to be acknowledged and never run.
- cn1.task.executor.<name>.kind is trimmed, and anything but platform or
virtual is refused: at start-up for the kinds the files set, and when the
executor is created for one set only in the environment. A typo used to
fall back silently to the annotation's kind.
- An Error that fails the work of inSpan or inBackground -- custom spans,
@Async calls, scheduled runs -- is recorded on the span before it is
rethrown, rather than the span exporting as a success.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: histogram shapes, empty batches, untraced servers, one metrics exporter
- A histogram registered again under an existing name must have the same
bucket boundaries and label keys; a different shape is refused rather
than silently recording against the first one's buckets and labels.
- MCP: an empty JSON-RPC batch is answered with Invalid Request, not the
202 an all-notification batch gets.
- A backend with tracing off passes an untraced marker instead of null, to
its listener, its request handling, websocket callbacks, scheduler and
@Async calls. Null means "whatever tracer is installed", so another
backend in the process installing one exported this server's requests
and jobs under its own service name.
- Only the first open metrics exporter of an identity exports; a second one
with the same service and endpoint waits and takes over when the first
stops, instead of sending every point twice. The same exporter opened
twice is refused.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: tokenless MCP on loopback, JSON-RPC 2.0 only, DST folds, bridge ids
- A development server whose MCP endpoint has no token binds 127.0.0.1 when
no address was chosen, and refuses to start when one was chosen that is
not loopback. The development tools write SQL and call every handler,
and the listener used to bind every interface. Documented in the guide
and the skill, with how to test from another machine.
- MCP refuses a request whose jsonrpc is not exactly "2.0" before its
method runs.
- A cron time the clocks pass twice (01:30 on a fall-back night) fires at
the first occurrence, as Spring and Quartz do; the second only when the
search starts after the first. It used to take the second.
- The stdio bridge answers an HTTP refusal -- a 401 for a missing token,
most often -- under the host's own request id. The server's error came
before it read the body, so it carried a null id and the host never saw
its request complete.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: spare pre-rotation ids, answer malformed MCP, drain before metrics off
- A session rotated by a request still running is spared under the id its
row and cookie still have, as well as its new one: a purge or another
request's lookup used to see only the new id as busy and delete the row
the running request was about to move.
- MCP: an id-less object that is not a valid request (no method, or not
JSON-RPC 2.0) is answered with Invalid Request under a null id; only a
valid notification goes unanswered. A response object is ignored.
- The stdio bridge answers a batch the backend refused or never received
with one error per request id in it.
- A request handler, websocket callback or executor task that calls
Backend.stop() while another stop is under way returns instead of
waiting: waiting held the very work the first stop's drain waited for.
- Server metrics are turned off after the background-task drain, so a
scheduled run that ends during it still records its duration.
- A start that fails after the listener bound closes the session-scoped
beans a request may already have built.
- A float argument too small for a float is refused rather than made 0.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: strict loopback, JSON-RPC id types, @Async scope, savepoint failures
- A tokenless MCP listener's address must be loopback by definition:
localhost, ::1, or a numeric 127/8 literal. A prefix test took a name
such as 127.backend.example for loopback wherever it resolved.
- The MCP start-up line advertises the address the listener is bound to
(bracketed for IPv6), not 127.0.0.1 regardless.
- MCP refuses a request whose id is an object, array or boolean, with a
null id and before its method runs.
- Beans with @Async methods join the scoped-dependency check: their tasks
run on an executor with no request current, so a request- or
session-scoped bean reached from one throws on first use.
- A savepoint that cannot be set marks the outer transaction
rollback-only, so a caller that catches the failure cannot commit half
of a flush or report success for work the server rolled back.
- A metrics exporter hand-over starts the successor only while it is
still open and first in line, under the lock its own shutdown takes; a
successor that stopped meanwhile used to get a thread nothing stopped.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: retire shared session beans, drain gauge reads, bound-address calls
- An invalidation no longer destroys session-scoped beans another request
still holds -- a database store hands each request its own copy. The
beans are retired: that request keeps using them rather than building
new ones under the deleted id, and the last one to leave destroys them.
- Removing a shared gauge source waits (bounded) for reads of it already
running, and a read skips a source removed since its list was copied;
the server's own exporter now shuts down before its beans are destroyed
and the pool closed, not after. A gauge used to be called during its
bean's @PreDestroy, or against a closed pool.
- backend_call sends its request to the address the listener is bound to
(Backend.getListenAddress), not 127.0.0.1 regardless.
- A histogram decides whether to export its unlabelled series under the
lock that adds labelled ones, so a racing first observation no longer
adds a transient zero series.
- A task virtual thread abandoned at the drain deadline before its first
turn has its token removed from VIRTUAL_TASKS, which otherwise kept the
task and everything it captured for the life of the process.
- An executor whose virtual hand-off is refused wakes a shutdown() waiting
on the count it just gave back.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: per-source gauge drains, executor kinds, unannounced rotations, params
- Shared-gauge reads are counted per source, so a gauge stuck in its
callback delays only its own removal rather than every gauge a stopping
server removes, two seconds each.
- One named executor asked for two kinds of thread is refused: at build
time between @Async and @Scheduled declarations, and at run time for
executors asked for in code. Whichever ran first used to decide, so a
PLATFORM database method could run on the virtual hosts. AUTO agrees
with either, and configuration still overrides.
- A gauge without a name is refused, as counters and histograms are; it
rendered a nameless Prometheus sample the scraper rejected.
- A rotation a failed request cannot announce is undone on the session
itself, and the request's other changes are saved under the id the
client still has. Only the beans used to move back.
- MCP params that are not an object are Invalid params, not treated as an
empty call.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: one cron run per repeated time, strict MCP args, masking, DevTools reuse
- A cron time the clocks pass twice fires once, at the first occurrence,
from whichever side of the change the search starts. It used to skip to
the next day from the old offset and return the repeat from the new one,
so the answer depended on which side of the change the search began. After
a run at the first, the scheduler searches from just past it, where
returning the repeat would run a daily job twice. The search steps past
the repeat within the day, so the other times of that day still count.
Documented in the scheduling chapter.
- tools/call arguments that are not an object are Invalid params; the tool
used to run with none.
- backend_config masks keys naming headers or auth, and values carrying a
credential as name=value -- cn1.otel.headers=api-key=..., a datasource
URL's ?password=... -- whatever their key.
- One DevTools instance given to two servers no longer points the first
server's tools at the second: a later installation gets its own instance.
- The MCP and management paths are canonicalized as request targets are
(Config.getRoutePath), so /%6dcp is served at /mcp instead of never.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: tear down after an in-flight stop caller, one server per builder
- A stop() called from a request, a websocket callback or a task drains as
before -- discounting its caller -- but defers the teardown (beans
destroyed, pool closed) to a thread that waits, up to the shutdown
timeout, for that caller to leave. Requests and callbacks are counted in
and out for it. The caller used to go on running against destroyed beans
and a closed pool, and its own request stored its session into them. The
tracer's shutdown is arranged on the caller's request span first, so that
request still exports its trace.
- A histogram ignores values past its label keys when telling series apart;
a one-label histogram recorded with two second values exported two series
under one label set.
- A new session whose first save throws has its session-scoped beans
destroyed and any half-written row removed: no cookie was sent, so nothing
could reach it again.
- Backend.Builder.sessionStore installs a store before the server listens,
and Sessions.setStore refuses once a session has been looked up: a store
set after start() lost the sessions already made.
- A builder refuses to start a second server while its first is running:
both shared the generated application's beans and scheduler, and
stopping the first destroyed the second's. It may start again after.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: one transaction one pool, unwind overrunning tasks, session bean edges
- A @Transactional method bound to one pool that touches another is refused
(TransactionException.IllegalState), naming REQUIRES_NEW and NOT_SUPPORTED.
The second pool used to hand out an ordinary connection that committed
each statement, so a failure rolled back only the first database.
- A virtual task still running at the drain deadline is no longer freed:
free() does not unwind, so its finally blocks and monitor exits never ran
and a lock it held stayed owned. A task that never started is still
dropped; one that started runs on to completion past the deadline, as an
overrunning platform task does, while the stop proceeds.
- A host's wake byte is written, and its wake pipe closed, under the inbox
lock, so a submitter cannot write into a descriptor shutdown has closed
and the process has reused.
- A request- or session-scoped bean inheriting an @Async method is refused
like one declaring it.
- A copy that rotates after another request invalidated its session uses
the retired beans rather than building a set under the new id.
- A histogram recorded with no label values goes to its unlabelled series
rather than a second one with the same empty labels.
- A database session's bean holder gets the touch-interval grace its row
gets, so a request in that window does not find its beans destroyed and
build a second set.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: conditional @Primary fallbacks, scoped factory owners, metric labels
- A conditional @Primary no longer discards the other candidates: the
wiring uses it when its condition holds and the active alternative when
not (Wiring.preferred). A prod-only primary beside a dev bean used to
make the dev profile fail to start. More than one unconditional
alternative is refused at build time.
- A non-static @Bean method on a request- or session-scoped configuration
that builds a bean of another scope is refused at build time: it is
called through the scoped stand-in at start-up, with no request to find
it in, and the server refused to start.
- Histogram label values are widened before choosing a series (an Integer
and a Long 200 export alike), the overflow label key is reserved --
also in the spelling Prometheus folds it to -- and Gauge.point refuses an
empty label key. Each produced duplicate or nameless samples that make a
scraper reject the whole exposition.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: joined failures mark rollback-only, AUTO decided per task, napping waiters, exporter hand-off
- A joined DataSource.inTransaction or EntityManager.transaction body that
throws now marks the outer transaction rollback-only, as Spring's
participating transactions do; an outer caller that swallowed the
exception used to commit the helper's half-done writes.
- An AUTO executor decides virtual-or-platform per submission, not at
creation, so one first requested during start-up (before the hosts exist)
no longer stays on platform threads for good.
- A virtual thread waiting cooperatively on an @Async Future naps on its host
with a deadline (VirtualThread.current + HttpServer.napUntil) instead of
being re-queued at once; the host loop polls with the earliest nap as its
timeout rather than spinning a core.
- A replacement OTLP metric exporter's thread joins its predecessor before its
first export, so two exports of one registry never overlap.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: namespaced JDBC sessions, exporter transport check, start-up drain
- JDBC session rows carry a namespace (cn1.session.namespace, by default the
generated application's package) and every query is limited to the
store's own, so two different servers on one host sharing a database no
longer accept each other's session cookie. Replicas share a namespace.
- A second metrics exporter with different headers or protocol is refused
like one with a different service or endpoint: only one exports, so its
credentials were silently replaced by the first's. Header values are
never printed.
- A failed start drains the tasks @PostConstruct started with the configured
shutdown timeout before destroying their beans, instead of interrupting
them and tearing down at once.
- A metric reader that answered false from open() is neither kept nor shut
down; a reader whose shutdown throws no longer stops the teardown.
- A class-level @Async counts only for the methods its class declares, as
the weaver applies it, in the @Scheduled, @McpTool and scoped-bean checks.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: align with Spring on handler Errors, scope checks and shared sessions; review fixes
Spring Boot behaviour where nothing costs more to match it:
- A handler's Error is answered 500, as Spring Boot's Tomcat answers it,
instead of dropping the connection; only a VirtualMachineError other than
a stack overflow is rethrown, as Tomcat rethrows it.
- A websocket endpoint, job, managed bean or @Async bean that can reach a
request- or session-scoped bean, and @Async on a scoped bean, build with a
warning rather than an error: Spring starts them and the scoped stand-in
throws IllegalStateException only when used with no request current.
- JDBC sessions default to one shared namespace, as Spring Session shares
its table; cn1.session.namespace keeps two projects apart.
- A metric registered again in another unit keeps the first, as Micrometer
does, and is warned about once.
Review fixes:
- An AUTO executor is pinned to the kind a later caller asks for by name,
so the result no longer depends on call order.
- The task queue gauge reads only the servers that measure.
- An exporter opened a second time is refused before its settings change,
and a server whose reader's open() threw no longer shuts it down.
- A string tool argument refuses an array or object.
- A request leaves its sessions before restoring the task and trace
context, so a retired session's @PreDestroy runs as its own server's.
- A timed Future.get saturates its deadline instead of overflowing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: undo failed rotations, text-keyed histogram series, early JSON, optional conditionals
- A session-id rotation whose save fails is undone -- the id, and the beans
a lookup after the rotation moved -- so the client's old cookie still
finds them instead of a second set being built.
- Histogram series are told apart by the text Prometheus prints, so
Boolean.TRUE and "true", or 1L and "1", are one series, not duplicate
samples; the first value's type is kept for OTLP.
- respondJson's body is serialised before request-scoped beans are
destroyed, as Spring MVC writes the body first; only when there are beans
to end, so the zero-copy write is unchanged otherwise.
- An optional field or setter whose candidates are all conditional is
injected only when one is active, keeping the field's initializer as
Spring does; @Autowired(required = false) on a method now makes its
arguments optional, as in Spring.
- A route returning a Future (an @Async route) is refused with a message
that says why: the client would get the pending task, not the result.
- Caller-supplied waits saturate their deadline in every shutdown and borrow.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: saturated schedules, active-only routes, one setter per property, partial metric success
- Fixed-delay, fixed-rate and initial-delay schedules and lock leases
saturate their timestamps: a period of Long.MAX_VALUE wrapped into the past
and ran the job back to back.
- The route listing (backend_routes) names only the controllers and
endpoints this start registered; a conditional one that is off serves 404.
- @ConfigurationProperties binds one setter per property, as Spring Boot's
binder does: the overload matching the getter's type, else the first.
- The OTLP metric exporter reads partial_success and reports rejected data
points instead of counting the export healthy.
- The websocket wrapper forwards getSubprotocols(), so an endpoint's
protocols are negotiated.
- A metric name used by both @Timed and @Counted is a build error, rather
than a registration that throws after the body has run.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: non-blocking chained futures, handshake stop, tracer before accept, Future results; fix TelemetryTest flake
- An @Async call returning another's pending AsyncTask completes when that
one does instead of blocking its worker, which deadlocked a one-thread
executor (and any pool under enough concurrent outer calls). Any other
Future is still awaited, as Spring's interceptor does.
- The websocket handshake (router, getSubprotocols, onOpen) is marked as
serving work: a stop() from it discounts its own connection and defers the
teardown until it returns; the fallback router counts in flight too.
- A server's tracer, including the untraced marker, is set before any
worker accepts, so a request accepted meanwhile cannot report to another
server's tracer.
- An @McpTool or @ManagedOperation returning a Future is a build error, as a
route's is: the caller would get the pending task, not the result.
- TelemetryTest drains the network queue before each test and waits for its
own span: a previous test's late export landed in the collector mock and
failed build-test (21) intermittently.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: only the rotating request announces a session rotation; exporter reopen waits; Future attributes
- Memory-store sessions: a request records the id it looked the session up
by (its cookie), not the shared object's current id, and only the request
that called changeSessionId() stores and announces the rotation. A request
arriving mid-login used to undo the login's rotation -- or complete it and
send the new id to whoever held the old cookie -- defeating the fixation
defence.
- A reopened OTLP metric exporter waits for its previous thread's last export
before applying the new configuration, instead of starting a second loop
beside it; refused if that export outlasts a minute.
- An @ManagedAttribute getter returning a Future, or woven @Async, is a build
error, as operations and tools already are.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: Prometheus-folded aspect metric names, replaced shared gauges, prototype jobs warned
- @Timed/@Counted names are checked at build time the way the runtime claims
them for Prometheus: two names that fold alike (latency.ms, latency_ms), or
a counter's _total against a histogram's series, are refused, instead of
throwing in the woven finally after the body ran.
- A gauge that replaces one addSource() built drops the stale shared
registration, so the old server's last removeSource no longer deletes it.
- A prototype bean with @Scheduled methods builds with a warning, as Spring
runs it: one instance runs its jobs for the server's life and a prototype is
never destroyed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: old session id retired at rotation, request beans drained fully, interface aspects refused
- The memory store answers a lookup only when the session still has that id:
a login's changeSessionId() retires the old, possibly planted id at once,
as a servlet container does, instead of when the login request saves. An
undone rotation restores the id and the entry answers again.
- Request-scoped beans that a @PreDestroy builds are destroyed however deep
the chain goes (bounded), not just one pass further.
- @Transactional, @Async, @Timed or @Counted on a project interface -- on a
default method, an abstract one, or the type -- is a build error: the build
weaves classes, and the annotation was silently applied to nothing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: one backend per process; conditional deps checked at start as in Spring
- Backend.start() refuses a second live Backend in the same process (a
stopping one is waited for, so stop-then-start still works). Scaling out is
more processes -- sessions in the JDBC store, job locks in the database --
and the runtime's process-wide state (tracer, metrics and exporter, default
executors, virtual-thread hosts) assumes one server. The tests that exercised
two concurrent servers are removed; one test pins the rule.
- A required dependency whose candidates are all conditional is checked at
start, as Spring checks it (Wiring.single names the injection point); the
developer guide no longer claims the build proves coverage.
- A @Scheduled method returning a Future builds with a warning: its return is
ignored, as in Spring, and the run ends when the method returns.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: class-specific woven bodies, virtual-task stop, per-bean job names, full termination; build on master
- Moved method bodies are named per declaring class, so a same-package
subclass's woven override no longer overrides the base class's body: an
explicit super.foo() runs the base body instead of recursing.
- stop() called from a virtual @Async task no longer waits out the drain for
its own host: each virtual task records the host it runs on.
- Two beans of one class with @Scheduled methods (two @Bean factories) get
their jobs named by bean, as Spring schedules each, instead of refusing
the start with a duplicate name.
- awaitTermination() also waits for a deferred teardown, so a process does
not exit while @PreDestroy and the pool close are still running.
- The wake-pipe native reads the array payload through CN1_ARRAY_DATA:
master's 4-byte header (#5903) removed the data pointer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: injective support names, discarded file responses closed, restart cursor, slot released
- Woven body names carry the declaring class's own name, not its hash, and
support-class names encode `_` and `$` injectively: two classes whose names
shared a hash (Aa, BB), or Outer_Inner and Outer$Inner, no longer get one
name -- a recursing super call, or one helper replacing the other.
- A file response replaced by a 500 because the session could not be stored
closes its descriptor; only the writer closed it before.
- The virtual-task host cursor is reduced to the current server's host count,
so a restart with fewer hosts does not index past its array.
- A stopped Backend frees the process slot, so its destroyed beans and
sessions are not kept reachable until the next start.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend build: injective support names, per-bean stand-ins, lifecycle runs synchronously
- Support-class names escape `_` as `__` and `$` as `_S`, so no two class
names meet (A$_B and A_$B collided under the previous scheme).
- Scoped and lazy stand-ins are named per bean: two @Bean methods of one
class, both request-scoped, each get their own instead of one source
replacing the other.
- A stand-in whose bean's constructor calls an overridable method runs the
bean's own code during construction rather than dereferencing a scope that
is not assigned yet.
- @Async on @PostConstruct or @PreDestroy is not applied, with a warning, as
Spring calls lifecycle methods on the bean itself: the server no longer
becomes ready before initialisation, and a @PreDestroy body still runs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend review fixes; recalibrate the Windows AMD perf row; GcSteadyState fault twin may wedge
Review:
- A request's scoped beans are destroyed even when serialising the deferred
JSON body throws: the array was taken first, and every later pass found
nothing to destroy.
- @Profile("!") (an empty negated name) is a build error; it activated the
bean under every profile.
- A @PostConstruct or @PreDestroy returning a Future builds with a warning:
the return is ignored, as in Spring, and the server goes on when the method
returns.
Gates:
- perf-baseline.json: the windows-x64 AMD family 25 model 1 rows are
recalibrated with calibrate-perf-baseline.py from the six calibration runs
of #5903 plus this branch's run, which changes nothing in the VM. Its
arrayRandom rounds spanned 1.06-1.49 inside one run (median 1.38 against a
0.97 baseline) -- runner noise the row's 25% tolerance could not absorb; the
row is now 1.061 with 45%, the script's own rule for the observed spread.
- GcSteadyStateIntegrationTest scenario 4 builds the reserve OUT
(-DCN1_PACING_NO_RESERVE) to prove scenario 3 can fail. That deliberately
broken build can wedge on the admission margin instead of finishing, and the
gate required it to finish, so it failed on a slow runner (footprint
745MB of a 768MB budget, no cycles, no reserve parks). A wedge is now
accepted as the demonstration, checked from the per-second probe: headroom
under the threshold and no reserve parks. A finishing run is held to the
same assertions as before.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend docs: drop unsupported hand-written mains, promote Future, db session store
- Remove every hand-wired server from the guide (ServerSnippets, raw
HttpServer.start/serveForever, installByHand, mcpTool builder); the build
writes the entry point and that is the only supported shape.
- Promote java.util.concurrent Future/ExecutionException/TimeoutException/
CancellationException/TimeUnit (and Properties) to documented backend API:
rendered in the backend javadoc, gated by check-backend-jdk-surface.py
(which also scans the guide's demos), and exercised on ParparVM by
SelfTest.futures().
- Session store value "jdbc" -> "db"; diagrams clarified (no bare "null",
fixedRate vs fixedDelay with varied run lengths).
- ParparVM virtual threads are not Java 21 ones; say so.
- Correct doc claims the audit found false: CN1_ prefixed env names,
multi-server passages (one backend per process), @Scheduled scope rules,
management "always on in dev", injected DataSource instead of
DataSource.open(getenv), PushFeedback on the backend Json and injection.
- Document the missing cn1.otel.* keys and CN1_HTTP_MAX_UPLOAD_MB.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: management and MCP linked only on request; settings as annotations
- Backend no longer names Management or McpServer. Only the builder's
management() and mcp() do, and the generated entry point calls them only
when the build asked (@EnableManagement / @EnableMcpServer / an @McpTool /
a literal cn1.*.enabled=true in any application*.properties, or a dev
build). The translator drops the uncalled builder method and the classes
with it; BackendOtelTest now asserts from nm that a server that never asked
carries neither, against a control that links both.
- Typed settings annotations -- @ServerConfig, @SessionConfig,
@DataSourceConfig, @StaticFilesConfig, exporter settings on
@OpenTelemetry, paths on the Enable* ones -- compiled in as the bottom
configuration layer (Config.withCompiledDefaults), under the files and the
environment. Values the runtime would refuse, and two classes disagreeing,
are build errors naming the class.
- OtlpTracerTest.partialSuccessIsCounted waited on a flush that could run
before the request's span was queued (the span ends after the response is
written); it now waits for the span first.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: controllers return and accept the application's own classes as JSON
A route may now return an entity or DTO (and collections or String-keyed
maps of them) and take one as @RequestBody, as a Spring controller does
through Jackson. The build writes a <Name>Cn1Json codec per class -- plain
code, no reflection -- in the app's @Mapped JSON form, so a class shared by
app and backend round-trips: fields (public directly, others via bean
accessors), @JsonProperty/@JsonIgnore (now shared with the backend), Date as
epoch millis (read from millis or ISO-8601), byte[] base64, enums by name,
subclasses written as themselves, unknown members ignored.
- A body the codec refuses is a 400 naming the path
($.lines[0].quantity: expected ...); a response nesting past 64 objects
(a cycle) is a 500 pointing at @JsonIgnore.
- Generic type-variable fields, body classes without a no-arg constructor,
interfaces, arrays other than byte[], and runtime types such as
HttpServer.Response inside a list stay build errors, with the reason.
- HttpServer renders a deferred JSON body before the write, into the same
buffer, so a throw there (a Writable, a codec) is answered 500 and recorded
as the handler's failure instead of dropping the connection unanswered.
- The guide's order example is compiled from the guide's own files and run
end to end by BackendBeansTest.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: outbound I/O parks a virtual thread instead of blocking its host
A virtual thread used to park only on the socket it served. An outbound
read -- a PostgreSQL or MySQL query, a Web call, a TLS handshake -- did a
blocking recv() on the host pthread, and with one host per core that many
slow calls stopped the server answering anything.
- Native: a per-virtual-thread wait record (descriptors, events, timeout)
and cn1BackendVtWait/cn1BackendAwaitFd; resume answers WAITING (3) for
it. Outbound Tcp descriptors stay non-blocking for life and every wait
(connect, read, write, TLS handshake/read/write) parks on a virtual
thread and polls anywhere else, so platform threads behave as before.
SO_RCVTIMEO/SO_SNDTIMEO become the wait's deadline. Web drives libcurl
through the multi interface on a virtual thread.
- Host: WAITING registers the descriptors with the host's own poller for
exactly the wait, wakes on readiness or deadline, and the stop drain
keeps pumping tasks that wait on outbound I/O.
- Docs/javadoc/skill: network databases, Web and TLS are fine on VIRTUAL;
SQLite, file access, host-name resolution and Object.wait still block.
- Tests: SelfTest (Tcp, Web, TLS handshake, TLS read, read deadline) and
DbCheck (pg_sleep / SLEEP against a one-host server) prove another
request is served while the call waits; both fail with the old
blocking wait.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: clear the PMD report; native checks for the JSON codec runtime
- The seven AvoidUsingHardCodedIP findings are loopback and wildcard
literals that recognise or bind the local interface and dial nothing;
each carries a //NOPMD with that reason, the convention ServerSocket and
Credentials already use. The OwnRoute overrides get @Override.
- SelfTest exercises JsonCodec on the translated runtime: range and
fraction refusals with their paths, dates from millis and ISO-8601 with
offsets and fractions, an impossible date refused, base64, writeDate.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend review: codec value dispatch, TreeSet, long bounds, MCP CORS and paths
- An Object or raw Map/List field is written by its run-time class through a
generated cn1app.JsonValues: a class the build writes goes through its
codec, Date as millis, enums by name, JDK shapes through Json; any other
class is a 500 instead of its toString().
- A TreeSet body needs a Comparable element; refused at build time otherwise.
- readLong bounds a fractional-free double by 2^63 before converting, so
9223372036854775808.0 is refused instead of clamped to Long.MAX_VALUE.
- MCP and management match the canonical path (pathFrom), as the routers,
static files and relay do: /%6dcp is /mcp.
- MCP answers an allowed origin's CORS preflight before authentication and
names the origin on every reply to it (new Response.header).
- The guide's order example uses one source block per include, which the
snippet validator requires.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend review: VT pinning, pool opens outside the monitor, MCP codecs, lease check
- PINNING. ParparVM identifies a monitor's owner by its OS thread, which
every virtual thread on a host shares, so a virtual thread that parked
inside `synchronized` let the next one on that host into the same critical
section. With outbound I/O now parking, that became likely. A virtual
thread now counts the monitors it holds (one TLS load per monitorEnter/
Exit, zero off a virtual thread) and never switches out while the count is
above zero: every park -- socket wait, collector handshake, nap -- asks
cn1BackendCanPark/cn1VirtualThreadPinned and waits like a platform thread
instead. SelfTest reproduces the interleave on one host: two requests
waiting on a slow socket inside one monitor; with pinning disabled it sees
two inside at once.
- DataSource reserves pool capacity under its monitor and opens the
connection outside it, and a virtual thread waits for a free connection by
napping rather than wait(); DbCheck runs concurrent lazy opens on one host
against real PostgreSQL.
- @McpTool parameters other than scalars are read through the shared JSON
codecs (List<Integer> holds Integers, DTOs are built, a bad value is a tool
error naming its path), results are written through them, and shapes they
cannot serve are build errors. The codec set is shared between the bean
and router processors.
- A scheduler claim that lands after its lease ran out is not run.
- Response.header no longer fully qualifies an imported class (forbidden PMD).
- The DB session purge's cross-replica limit is recorded as a Spring-matching
decision.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend: a connection gate instead of Database's monitor, so queries stay parkable
With virtual threads pinned while they hold a monitor, Database's
synchronized methods pinned every query, and a PostgreSQL or MySQL wait held
its host again -- DbCheck's "another request is served while a query waits"
failed on one host. The exclusion a connection needs (one operation at a
time, a transaction reserved to its caller) is now a reentrant gate: a flag
taken and returned under a monitor held only for that instant. A virtual
thread waiting for it naps; anything else waits on the monitor as before.
Transaction ownership is keyed by the virtual thread, not
Thread.currentThread(), which every virtual thread on a host shares, and a
caller waiting on another's transaction gives the gate up meanwhile, as
wait() gave up the monitor.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend review: float range on body reads, built-in metric names reserved
- A body value read into float or Float is refused (400) when a float
cannot hold it, instead of narrowing 1e100 to infinity or 1e-100 to zero;
the same rule MCP float arguments already had.
- @Timed/@Counted names are checked against the server's own instruments at
build time, by name and by Prometheus series. Taken at run time, the clash
surfaced in the woven finally after the body's work had committed.
- An Object or raw collection field holding a class with no codec stays a
run-time 500 by design; the reasoning is in BackendJsonCodecs.check.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend build: fail fast on generated-name collisions; two dispatch/entry fixes
- Two classes whose JSON codec names fold together (Outer_Inner and
Outer.Inner) are a build error instead of one codec silently replacing
the other.
- Every class the bean processor generates -- aspects, proxies, tool and
managed adapters, codecs -- is checked against the project's own classes
before it is compiled; an existing class of that name is a build error
instead of being overwritten in the output directory.
- Subclass dispatch walks ancestors on the compile classpath too, so a
project subclass reaching the declared type through a dependency's class
is written with its own fields.
- A module whose only request is @EnableManagement gets an entry point.
Each has a test asserting the failure or the outcome; the dispatch and
entry-point tests fail with their fixes reverted.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend review: managed results through the codecs, sessions on websocket fallbacks
- An @ManagedOperation returning anything beyond a scalar or String is
written through the JSON codecs; one returning a shape they cannot write
is a build error. A DTO result was sent as its quoted toString().
- A websocket fallback router sees the server's sessions on the handshake
(the upgrade bypasses the request wrapper), and what the handshake did to
them is stored and released afterwards.
- perf-baseline: calibrate linux-x64@amd-epyc-9v45-96-core-processor, a
runner CPU model the gate had no row for (ratios within the spread of the
existing AMD/Intel linux rows).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Backend review: gauge-only managed attributes, static-only and root-mounted servers
- An @ManagedAttribute that is not a number or a boolean is a build error;
it was listed under /manage/managed and silently missing from metrics.
- A module that only serves static files (@StaticFilesConfig or
cn1.static.root in a properties file) gets an entry point.
- cn1.management.path=/ mounts the endpoints at the server root instead of
being refused as not starting with /.
Each has a test; the two outcome tests fail with their fix reverted.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* perf-baseline: 30% RAM tolerance on linux-arm64 stringBuilding
The arm64 gate flagged stringBuilding RAM at 0.32x against a 0.26x
baseline. Not a regression: the VM, JavaAPI and benchmark are identical
between this head and 3666deb288, which read 0.27x, and master reads 0.26x
on every run -- the same code, two readings, one row's RAM moving between
runs. The baseline stays 0.26x; the row gets the 0.3 memory tolerance other
noisy RAM rows carry, so +30% is still a regression.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>1 parent 656ea3d commit 65ae4d8
224 files changed
Lines changed: 38631 additions & 1501 deletions
File tree
- .github/scripts
- CodenameOne/src/com/codename1/annotations
- docs
- demos/backend/src/main/java/com/codenameone/developerguide/backend
- beans
- orders
- developer-guide
- img
- maven
- backend
- src/test/java/com/codename1/backend
- mcp
- otel
- cn1app-archetype/src/main/resources/archetype-resources/backend
- src/main/java
- codenameone-maven-plugin
- src
- main
- java/com/codename1/maven
- annotations
- processors
- resources/META-INF/services
- test/java/com/codename1/maven/processors
- core-unittests/src/test/java/com/codename1/telemetry
- integration-tests
- scripts
- initializr/common/src
- main/resources
- skill
- references
- test/java/com/codename1/initializr/model
- vm
- ByteCodeTranslator/src
- JavaAPI/src/java/util/concurrent
- backend
- demo
- dbcheck/com/demo
- oteltest/com/demo
- selftest/com/demo
- impl
- javase/com/codename1/backend
- mcp
- parparvm/com/codename1/backend
- native
- src/com/codename1
- backend
- annotations
- mcp
- metrics
- orm
- otel
- sql
- impl/orm
- selfhost
- tests/src/test/java/com/codename1/tools/translator
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
172 | 172 | | |
173 | 173 | | |
174 | 174 | | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
175 | 188 | | |
176 | 189 | | |
177 | 190 | | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
178 | 194 | | |
179 | 195 | | |
180 | 196 | | |
| |||
185 | 201 | | |
186 | 202 | | |
187 | 203 | | |
188 | | - | |
| 204 | + | |
189 | 205 | | |
190 | 206 | | |
191 | 207 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
32 | 37 | | |
33 | 38 | | |
34 | 39 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
33 | 37 | | |
34 | 38 | | |
35 | 39 | | |
| |||
Lines changed: 2 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
| 37 | + | |
38 | 38 | | |
39 | | - | |
40 | | - | |
| 39 | + | |
41 | 40 | | |
42 | 41 | | |
43 | 42 | | |
| |||
Lines changed: 9 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
38 | | - | |
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | | - | |
| 45 | + | |
| 46 | + | |
46 | 47 | | |
47 | 48 | | |
48 | 49 | | |
| |||
58 | 59 | | |
59 | 60 | | |
60 | 61 | | |
61 | | - | |
62 | | - | |
63 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
64 | 68 | | |
65 | | - | |
66 | 69 | | |
67 | 70 | | |
68 | 71 | | |
| |||
Lines changed: 4 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
| 26 | + | |
25 | 27 | | |
26 | | - | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
30 | | - | |
| 31 | + | |
| 32 | + | |
31 | 33 | | |
32 | 34 | | |
33 | 35 | | |
| |||
Lines changed: 91 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
Lines changed: 22 additions & 10 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
30 | | - | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
57 | 58 | | |
58 | 59 | | |
59 | 60 | | |
60 | | - | |
61 | | - | |
| 61 | + | |
62 | 62 | | |
63 | | - | |
64 | | - | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
65 | 76 | | |
66 | 77 | | |
67 | 78 | | |
68 | 79 | | |
69 | 80 | | |
70 | 81 | | |
71 | 82 | | |
72 | | - | |
| 83 | + | |
73 | 84 | | |
74 | 85 | | |
75 | 86 | | |
76 | 87 | | |
77 | 88 | | |
78 | | - | |
| 89 | + | |
79 | 90 | | |
80 | 91 | | |
81 | 92 | | |
| |||
118 | 129 | | |
119 | 130 | | |
120 | 131 | | |
121 | | - | |
122 | | - | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
123 | 135 | | |
124 | 136 | | |
125 | 137 | | |
| |||
156 | 168 | | |
157 | 169 | | |
158 | 170 | | |
159 | | - | |
| 171 | + | |
160 | 172 | | |
161 | 173 | | |
162 | 174 | | |
| |||
Lines changed: 0 additions & 9 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
26 | | - | |
27 | 25 | | |
28 | 26 | | |
29 | 27 | | |
30 | 28 | | |
31 | | - | |
32 | 29 | | |
33 | 30 | | |
34 | 31 | | |
| |||
51 | 48 | | |
52 | 49 | | |
53 | 50 | | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | 51 | | |
Lines changed: 0 additions & 31 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | 25 | | |
30 | 26 | | |
31 | 27 | | |
| |||
58 | 54 | | |
59 | 55 | | |
60 | 56 | | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | 57 | | |
75 | 58 | | |
76 | 59 | | |
| |||
143 | 126 | | |
144 | 127 | | |
145 | 128 | | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | | - | |
159 | | - | |
160 | 129 | | |
0 commit comments