Why does EK9 complain that my application registers services but the program creates no HTTP?
← Web Services · Ref: Q1375
A registered service is only reachable while something serves it. If the program linked to an application never creates an HTTP instance, the program runs its body and EXITS — the service is constructed, consumes its dependencies, and can never receive a request. Nothing at runtime reports this: the process exits successfully, so the mistake stays invisible until someone notices the endpoint was never live. EK9 raises E12020 at compile time instead.
TWO DIRECTIONS OF ONE MISTAKE
E12020 is the mirror of E12021. E12021 fires when a program creates HTTP but its application registers no services (a server with nothing to route to). E12020 fires when the application registers services but the program creates no HTTP (services with nothing serving them). Both are decidable at compile time because the application's registrations and the program's variable declarations are already resolved.
THE TWO FIXES
Either serve them — create NetworkProperties, an HTTP instance, and call serve() — or SPLIT the application. Splitting is the right answer when the program genuinely needs only the components: give it an application registering just those, and keep the service registration in the application used by the serving program. Conflating serving wiring with component wiring is exactly what leaves a REST service registered and permanently unreachable.
Example
defines module qa.webservices.serviceswithouthttp defines constant plainText <- "text/plain" defines service GreetService :/greet open hello() as GET for :/hello <- response as HTTPResponse: () with trait of HTTPResponse override status() as pure <- rtn as Integer: 200 override content() <- rtn as String: "hello" override contentType() as pure <- rtn as String: plainText default operator ? defines component Repository as abstract describe() as pure abstract <- rtn as String? InMemoryRepository extends Repository override describe() as pure <- rtn as String: "in-memory" defines application // Serving wiring: registers the service, so its program MUST create HTTP. GreetApp register InMemoryRepository() as Repository register GreetService() // Component wiring only: safe for a program that does not serve. RepositoryOnly register InMemoryRepository() as Repository defines program ServeIt() with application of GreetApp stdout <- Stdout() props <- NetworkProperties(19099) httpServer <- HTTP(props) stdout.println("serving") httpServer.serve() ReportOnly() with application of RepositoryOnly repository as Repository! stdout <- Stdout() stdout.println(repository.describe())
Common mistakes
E12020 — GreetApp registers GreetService, so a program wired to it must create and serve an HTTP instance. Without one the program prints and exits and the service is never reachable. See ek9 -h E12020 for details.
Incorrect:
NoServe() with application of GreetApp stdout <- Stdout()
Correct:
ServeIt() with application of GreetApp props <- NetworkProperties(19091) httpServer <- HTTP(props) httpServer.serve()
E12020 — When a program needs only the components, split the application: keep the service registration in the application the serving program uses, and give the non-serving program one that registers only what it needs. See ek9 -h E12020 for details.
Incorrect:
AccessPoint
register InMemoryRepository() as Repository
register Addresses()
Correct:
RepositoryOnly
register InMemoryRepository() as Repository
Other ways to ask this
- What is E12020 in EK9?
- I registered a service but never see it respond — why?
- Can I use an application that registers services from a non-serving program?
- Why must a program with a service application create HTTP?
- How do I share components with a program that does not serve HTTP?
Coming from another language?
Spring Boot: @RestController beans are picked up by the embedded server automatically, so 'registered but never served' cannot easily arise — the framework always starts a server. JAX-RS/Jersey: resources are registered in an Application subclass and served by the container; an unserved resource is a deployment error found at runtime. Node/Express: routes are attached to an app that must still call listen() — forgetting listen() is the same bug, but it is only visible when the process exits. EK9: the compiler rejects it, so the process can never be built in that state.
Keywords: injection, E12020, E12021, application, wiring, web, component, serve, http, rest, service, register