diff --git a/packages/hawtio/src/plugins/shared/jolokia-service.test.ts b/packages/hawtio/src/plugins/shared/jolokia-service.test.ts index 3d1769b41..1de79e42c 100644 --- a/packages/hawtio/src/plugins/shared/jolokia-service.test.ts +++ b/packages/hawtio/src/plugins/shared/jolokia-service.test.ts @@ -2,7 +2,7 @@ import { userService } from '@hawtiosrc/auth' import Jolokia, { ListRequestOptions } from 'jolokia.js' import { DEFAULT_MAX_COLLECTION_SIZE, DEFAULT_MAX_DEPTH, JolokiaListMethod, jolokiaService } from './jolokia-service' import { hawtio } from '@hawtiosrc/core' - +// import { readFile } from 'node:fs/promises' describe('JolokiaService', () => { beforeEach(() => { jest.resetModules() @@ -94,4 +94,25 @@ describe('JolokiaService', () => { expect(options.maxDepth).toEqual(3) expect(options.maxCollectionSize).toEqual(10000) }) + + test('problematic JSON response from case hawtio/hawtio#3401', async () => { + // const content = await readFile("/data/sources/_testing/grgr-test-many-mbeans/src/test/resources/responses/004-formatted.json").then(v => v.toString()) + // jolokiaService.unwindListResponse(JSON.parse(content).value) + const response = { + value: { + 'java.util.logging': { + 'type=Logging': { + class: 'sun.management.ManagementFactoryHelper$PlatformLoggingImpl', + desc: 'Information on the management interface of the MBean', + }, + }, + 'my-domain-with-vanishing-mbeans': { + 'type=Bean1': { + error: 'javax.management.InstanceNotFoundException: Bean1', + }, + }, + }, + } + jolokiaService.unwindListResponse(response.value) + }) }) diff --git a/packages/hawtio/src/plugins/shared/jolokia-service.ts b/packages/hawtio/src/plugins/shared/jolokia-service.ts index 953256fa6..23737c0d7 100644 --- a/packages/hawtio/src/plugins/shared/jolokia-service.ts +++ b/packages/hawtio/src/plugins/shared/jolokia-service.ts @@ -32,7 +32,7 @@ import $ from 'jquery' import { define, func, is, object, optional, record, string, type } from 'superstruct' import { PARAM_KEY_CONNECTION, PARAM_KEY_REDIRECT, connectService } from '../shared/connect-service' import { log } from './globals' -import { OptimisedJmxDomain, OptimisedJmxDomains, OptimisedMBeanInfo } from './tree' +import { ErrorMBeanInfo, OptimisedJmxDomain, OptimisedJmxDomains, OptimisedMBeanInfo } from './tree' export const DEFAULT_MAX_DEPTH = 7 export const DEFAULT_MAX_COLLECTION_SIZE = 50000 @@ -540,9 +540,12 @@ class JolokiaService implements IJolokiaService { * @param response response value from Jolokia LIST * @param path optional path information to restore the response to {@link OptimisedJmxDomains} */ - private unwindListResponse(response: unknown, path?: string[]): OptimisedJmxDomains { + unwindListResponse(response: unknown, path?: string[]): OptimisedJmxDomains { const isOptimisedListResponse = (value: unknown): value is OptimisedListResponse => is(value, object({ cache: object(), domains: object() })) + const isMBeanInfoOrError = (value: unknown): boolean => { + return isMBeanInfo(value) || isMBeanInfoError(value) + } const isMBeanInfo = (value: unknown): value is OptimisedMBeanInfo => is( value, @@ -554,8 +557,9 @@ class JolokiaService implements IJolokiaService { notif: optional(record(string(), object())), }), ) + const isMBeanInfoError = (value: unknown): value is ErrorMBeanInfo => is(value, type({ error: string() })) const isJmxDomain = (value: unknown): value is OptimisedJmxDomain => - is(value, record(string(), define('MBeanInfo', isMBeanInfo))) + is(value, record(string(), define('MBeanInfo', isMBeanInfoOrError))) const isJmxDomains = (value: unknown): value is OptimisedJmxDomains => is(value, record(string(), define('JmxDomain', isJmxDomain))) diff --git a/packages/hawtio/src/plugins/shared/tree/node.ts b/packages/hawtio/src/plugins/shared/tree/node.ts index ab41a0b0e..2e4e0a7a7 100644 --- a/packages/hawtio/src/plugins/shared/tree/node.ts +++ b/packages/hawtio/src/plugins/shared/tree/node.ts @@ -25,6 +25,10 @@ export interface OptimisedMBeanInfo extends Omit { canInvoke?: boolean } +export interface ErrorMBeanInfo { + error: string +} + export interface OptimisedMBeanAttribute extends MBeanAttribute { canInvoke?: boolean }