|
| 1 | += Hawtio Architecture |
| 2 | + |
| 3 | +This page presents general information about the Hawtio architecture. We show all the components (client, server, agent connections). For details about plugin development, see xref::developers/applications.adoc[]. |
| 4 | + |
| 5 | +== Hawtio React Single Page Application |
| 6 | + |
| 7 | +Repository: https://github.com/hawtio/hawtio-next |
| 8 | + |
| 9 | +There are two main _parts_ of this repository: |
| 10 | + |
| 11 | +* https://github.com/hawtio/hawtio-next/tree/main/packages/hawtio[@hawtio/react] NPM package: a consumable Hawtio package with all Hawtio React components, services and APIs |
| 12 | +* https://github.com/hawtio/hawtio-next/tree/main/app[Hawtio application]: a _sample application_ that uses the above package. A similar application is available in https://github.com/hawtio/hawtio/tree/4.x/console[Hawtio console application] (used in Hawtio WAR and distributed as Java deployable application). |
| 13 | + |
| 14 | +Why the distinction? |
| 15 | + |
| 16 | +`@hawtio/react` package is a pure JavaScript/TypeScript _library_ bundled with https://tsup.egoist.dev/[`tsup`] and distributed as an https://www.npmjs.com/package/@hawtio/react[NPM package]. |
| 17 | + |
| 18 | +`app` application is using this package and is bundled as an _application_ using https://webpack.js.org[Webpack]. |
| 19 | + |
| 20 | +=== What are the elements of the `@hawtio/react` package? |
| 21 | + |
| 22 | +`@hawtio/react` package is a reusable _library_ that https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export[exports] JavaScript values, functions and classes. We can group these exported elements into two categories: |
| 23 | + |
| 24 | +* React components - should be imported from `.tsx` files and simply used with https://react.dev/learn/writing-markup-with-jsx[JSX] syntax. Most of the components use the https://v5-archive.patternfly.org/[Patternfly V5 library], but there are a few pure React/HTML components |
| 25 | +* JavaScript variables, functions and classes, as well as TypeScript type aliases, interfaces and enumerations. + |
| 26 | +Hawtio services (core services, plugin management, user management, ...) are exported as variables, which are instances of Hawtio classes. |
| 27 | + |
| 28 | +The most important Hawtio React/Patternfly component is `<Hawtio>` which is a full page with headers, sidebars and content areas. The UI, built by `<Hawtio>` component, depends on registered plugins. |
| 29 | + |
| 30 | +Hawtio includes built-in plugins. See available plugins at xref::plugins.adoc[] page. These plugins are _not_ registered by default. When creating an _application_ based on `@hawtio/react` _library_ we can choose whether to register all or selected Hawtio plugins. |
| 31 | + |
| 32 | +=== How to use the `@hawtio/react` package - minimal required setup |
| 33 | + |
| 34 | +In order to _use_ the Hawtio client library, you should create an application similar to the https://github.com/hawtio/hawtio-next/tree/main/app[Hawtio application]. |
| 35 | + |
| 36 | +At a minimum we should use these files: |
| 37 | + |
| 38 | +`index.ts`: |
| 39 | + |
| 40 | +[source,javascript] |
| 41 | +---- |
| 42 | +import '@hawtio/react/dist/index.css' |
| 43 | +import '@patternfly/react-core/dist/styles/base.css' |
| 44 | +
|
| 45 | +import('./bootstrap') |
| 46 | +---- |
| 47 | + |
| 48 | +`bootstrap.tsx`: |
| 49 | + |
| 50 | +[source,javascript] |
| 51 | +---- |
| 52 | +import React from 'react' |
| 53 | +import ReactDOM from 'react-dom/client' |
| 54 | +
|
| 55 | +import { Hawtio } from '@hawtio/react/ui' |
| 56 | +import { hawtio } from '@hawtio/react' |
| 57 | +
|
| 58 | +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) |
| 59 | +
|
| 60 | +hawtio.bootstrap().then(() => { |
| 61 | + root.render( |
| 62 | + <React.StrictMode> |
| 63 | + <Hawtio /> |
| 64 | + </React.StrictMode>, |
| 65 | + ) |
| 66 | +}) |
| 67 | +---- |
| 68 | + |
| 69 | +NOTE:: The separation is required because of the https://webpack.js.org/concepts/module-federation/[Webpack Module Federation] and usage of shared packages. See https://webpack.js.org/concepts/module-federation/#uncaught-error-shared-module-is-not-available-for-eager-consumption[the details here]. |
| 70 | + |
| 71 | +This setup is almost pure React code, where two steps are required |
| 72 | + |
| 73 | +* Call `ReactDOM.createRoot` to obtain the _root element_ |
| 74 | +* Render `<Hawtio>` React component for this root element |
| 75 | + |
| 76 | +The only difference is that we need to call the `hawtio.bootstrap()` function which initializes internal Hawtio services. |
| 77 | + |
| 78 | +As you can see, rendering the React component is performed after resolving the promise returned from `hawtio.bootstrap()`; this allows synchronization with Hawtio, so components are rendered after full initialization of Hawtio. |
| 79 | + |
| 80 | +To learn more about the stages of Hawtio initialization and how plugins are registered, read the full example below. |
| 81 | + |
| 82 | +=== How to use the `@hawtio/react` package - full setup |
| 83 | + |
| 84 | +Because the Hawtio _application_ should be bundled by Webpack, it is important to be aware of how such application is bundled. Without going into details, Webpack allows the splitting of all reachable JavaScript code into https://webpack.js.org/guides/code-splitting/[chunks]. Ideally, when a Single Page Application (SPA) starts, only necessary JavaScript code is fetched and evaluated, while remaining code is loaded on demand. |
| 85 | + |
| 86 | +JavaScript supports two kinds of _module imports_: |
| 87 | + |
| 88 | +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import[static import]: with `import ... from module-name` statement, we effectively include the imported module into the importing module. |
| 89 | +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import[dynamic import]: with `import('module-name')`, we can use the module asynchronously - `import()` operator returns a Promise resolved only when the imported code is loaded and evaluated. |
| 90 | + |
| 91 | +While browsers support ESM modules and both of these _import_ versions, Webpack transpiles this code into a more web-optimized version, while keeping the sync/async semantics of `import` and `import()`. |
| 92 | + |
| 93 | +Hawtio _applications_, (using `@hawtio/react` _library_), should carefully choose between static and dynamic imports. |
| 94 | + |
| 95 | +`@hawtio/react` uses Node.js https://nodejs.org/api/packages.html#subpath-exports[subpath exports] for the explicit specification of what can be imported from this package. Here is the relevant `package.json` fragment: |
| 96 | + |
| 97 | +[source,json] |
| 98 | +---- |
| 99 | +"exports": { |
| 100 | + ".": { |
| 101 | + "types": "./dist/index.d.ts", |
| 102 | + "require": "./dist/index.js", |
| 103 | + "default": "./dist/index.js" |
| 104 | + }, |
| 105 | + "./init": { |
| 106 | + "types": "./dist/init.d.ts", |
| 107 | + "require": "./dist/init.js", |
| 108 | + "default": "./dist/init.js" |
| 109 | + }, |
| 110 | + "./ui": { |
| 111 | + "types": "./dist/ui/index.d.ts", |
| 112 | + "require": "./dist/ui/index.js", |
| 113 | + "default": "./dist/ui/index.js" |
| 114 | + }, |
| 115 | + "./dist/index.css": { |
| 116 | + "default": "./dist/index.css" |
| 117 | + } |
| 118 | +}, |
| 119 | +---- |
| 120 | + |
| 121 | +Focusing on JavaScript only, we can import three distinct module locations: |
| 122 | + |
| 123 | +* `"@hawtio/react"`: this is the entry point with packages containing all Hawtio services, but no React (or Patternfly) components |
| 124 | +* `"@hawtio/react/init"`: this is the entry point that contains initialization code and the single `<HawtioInitialization>` React component which does not rely on Patternfly |
| 125 | +* `"@hawtio/react/ui"`: this is the entry point with React/Patternfly components, of which the most important is `<Hawtio>` |
| 126 | + |
| 127 | +How to import these packages? |
| 128 | + |
| 129 | +An _application_ that launches by rendering the `<Hawtio>` component should use the above entry points in the following way: |
| 130 | + |
| 131 | +From `@hawtio/react/init` we can statically import `hawtio`, `configManager` and the `<HawtioInitialization>` component |
| 132 | +to show initialization UI before importing the full package: |
| 133 | + |
| 134 | +[source,typescript] |
| 135 | +---- |
| 136 | +import ReactDOM from 'react-dom/client' |
| 137 | +
|
| 138 | +import { configManager, hawtio, HawtioInitialization, TaskState } from '@hawtio/react/init' |
| 139 | +
|
| 140 | +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) |
| 141 | +
|
| 142 | +root.render(<HawtioInitialization verbose />) |
| 143 | +
|
| 144 | +configManager.addProductInfo('Test App', '1.0.0') |
| 145 | +hawtio.addUrl('plugin') |
| 146 | +... |
| 147 | +---- |
| 148 | + |
| 149 | +`@hawtio/react` should be imported dynamically, so we can bootstrap it asynchronously: |
| 150 | + |
| 151 | +[source,typescript] |
| 152 | +---- |
| 153 | +import('@hawtio/react').then(async m => { |
| 154 | + // Register all default Hawtio plugins |
| 155 | + m.registerPlugins() |
| 156 | +
|
| 157 | + m.hawtio.bootstrap().then(() => { |
| 158 | + // ... |
| 159 | + }) |
| 160 | +}) |
| 161 | +---- |
| 162 | + |
| 163 | +Finally within `.then` block of the promise returned by `hawtio.bootstrap()` we can dynamically import the UI packages |
| 164 | +of Hawtio and render `<Hawtio>` component: |
| 165 | +[source,typescript] |
| 166 | +---- |
| 167 | +m.hawtio.bootstrap().then(() => { |
| 168 | + import('@hawtio/react/ui').then(m => { |
| 169 | + root.render( |
| 170 | + <React.StrictMode> |
| 171 | + <m.Hawtio /> |
| 172 | + </React.StrictMode> |
| 173 | + ) |
| 174 | + }) |
| 175 | +}) |
| 176 | +---- |
| 177 | + |
| 178 | +More information about writing Hawtio applications and plugins (including more information about how to register custom or built-in plugins) can be found at xref::developers/applications.adoc[]. |
| 179 | + |
| 180 | +== Hawtio Server |
| 181 | + |
| 182 | +Repository: https://github.com/hawtio/hawtio |
| 183 | + |
| 184 | +This repository has been used since the early 1.x release. Previously, the web application was a single Maven module that produced a Java WAR application. The rest of the modules were about integration with Jolokia and implementation of security filters, additional JMX MBeans and other items including the Git facade (for Fabric8). |
| 185 | + |
| 186 | +In Hawtio 4, this is still a very important project, which also includes a WAR application (that uses `@hawtio/react` NPM package), but there are more deployment options. The requirements for implementing with Fabric8 and OSGi have been removed. |
| 187 | + |
| 188 | +When describing xref:#_hawtio_react_single_page_application[], the https://jolokia.org/[Jolokia library] was not mentioned. However, this is a very important part of Hawtio's identity. |
| 189 | + |
| 190 | +Hawtio _server_ is effectively a server-side web application that exposes a Jolokia Agent REST API, which is then used by the Hawtio _client_ JMX plugin. |
| 191 | + |
| 192 | +There are 3 ways to _start_ (or _deploy_) a Hawtio server: |
| 193 | + |
| 194 | +* WAR: can be deployed to any standard Servlet container (Tomcat, Jetty, Wildfly, ...) |
| 195 | +* Spring Boot application: uses https://docs.spring.io/spring-boot/reference/web/servlet.html#web.servlet.embedded-container[Spring Boot managed web server] |
| 196 | +* https://quarkus.io/guides/web[Quarkus web application]: based on https://vertx.io/[Vert.x]. |
| 197 | + |
| 198 | +Whatever the deployment method, Hawtio server exposes several endpoints that support Hawtio _client_ applications. These endpoints may be provided by other means (server-side Node.js application, as in https://github.com/hawtio/hawtio-online[Hawtio Online]), so _this_ Hawtio server is optional. |
| 199 | + |
| 200 | +The most important _endpoint_ exposed is the Jolokia endpoint, which gives Hawtio client applications a _view_ into the MBeans available in a JVM MBeans. Remember that many plugins like `camel()` or `jmx()` simply won't work without Jolokia. |
| 201 | + |
| 202 | +== Remote Jolokia agents |
| 203 | + |
| 204 | +Finally, we need to describe a part of the Hawtio eco-system that allows: |
| 205 | + |
| 206 | +* Hawtio clients to access the Jolokia agents available from _different_ JVMs than the one serving the web resources of the Hawtio client |
| 207 | +* Hawtio server to act as a proxy between Hawtio client and remote Jolokia Agent. |
| 208 | + |
| 209 | +Remote Jolokia agents are JVM applications (like Camel JBang applications or Apache Artemis brokers with an enabled Jolokia agent), which do not include embedded Hawtio applications, but may be used through an Hawtio proxy. |
| 210 | + |
| 211 | +Again, knowing the history helps to understand this part of the architecture. When Hawtio was originally created, the Jolokia project had already been well-established, but had never had full UI support - it was purely a REST API exposing MBeans from a running JVM application. |
| 212 | + |
| 213 | +Hawtio, with its usage of Angular and JQuery Ajax, was built to access these Jolokia agents and their REST APIs, enabling the implementation of real brower UIs. |
0 commit comments