spacemonkd commented on code in PR #10900: URL: https://github.com/apache/ozone/pull/10900#discussion_r3896072387
########## ozone-ui/packages/om/src/pages/Overview/sections/JvmSection.tsx: ########## @@ -0,0 +1,253 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { Suspense, useMemo, useState } from 'react'; +import { + Button, + Dropdown, + Empty, + message, + Skeleton, + type MenuProps, + type TableColumnsType, +} from 'antd'; +import { DownOutlined } from '@ant-design/icons'; +import { Card, Chip, DataTable, Icon, KeyValuePair, Section, SearchInput } from '@ozone-ui/shared'; +import { + JMX_QUERY, + buildJvmHighlights, + parseJvmArguments, + toSystemPropertyRows, + type JvmParameter, + type JvmParameterCategory, + type RuntimeBean, +} from '../../../api/overview'; +import { useSuspenseJmxBean } from '../../../api/useJmx'; + +const highlightsGridStyle: React.CSSProperties = { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', + gap: '16px 24px', +}; + +const categoryColor: Record<JvmParameterCategory, 'blue' | 'orange' | 'neutral'> = { + 'System & Framework': 'blue', + 'Memory & GC': 'orange', + 'System Property': 'neutral', +}; + +const monospace: React.CSSProperties = { + fontFamily: "'Roboto Mono', monospace", + fontSize: 12, +}; + +const escapeXml = (s: string) => + s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); + +/** Render parameter rows as a Hadoop-style XML configuration snippet. */ +const buildConfigXml = (params: JvmParameter[]): string => { + const body = params + .map( + (p) => + ` <property>\n <name>${escapeXml(p.parameter)}</name>\n <value>${escapeXml( + p.value + )}</value>\n </property>` + ) + .join('\n'); + return `<configuration>\n${body}\n</configuration>`; +}; + +const columns: TableColumnsType<JvmParameter> = [ + { + title: 'Parameter', + dataIndex: 'parameter', + key: 'parameter', + width: '34%', + ellipsis: true, + render: (parameter: string) => <span style={monospace}>{parameter}</span>, + }, + { + title: 'Value', + dataIndex: 'value', + key: 'value', + width: '40%', + ellipsis: true, + render: (value: string) => <span style={monospace}>{value}</span>, + }, + { + title: 'Category', + dataIndex: 'category', + key: 'category', + width: '26%', + render: (category: JvmParameterCategory) => ( + <Chip color={categoryColor[category]} size="small"> + {category} + </Chip> + ), + }, +]; + +const JvmContent: React.FC = () => { + const { data: runtime, isEmpty } = useSuspenseJmxBean<RuntimeBean>(JMX_QUERY.runtime); + + const [search, setSearch] = useState(''); + const [category, setCategory] = useState<'All' | JvmParameterCategory>('All'); + const [showModules, setShowModules] = useState(false); + const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); + + const highlights = useMemo(() => (runtime ? buildJvmHighlights(runtime) : []), [runtime]); + + const allRows = useMemo<JvmParameter[]>(() => { + if (!runtime) { + return []; + } + const args = parseJvmArguments(runtime.InputArguments); + return showModules ? [...args, ...toSystemPropertyRows(runtime.SystemProperties)] : args; + }, [runtime, showModules]); + + const rows = useMemo(() => { + const needle = search.trim().toLowerCase(); + return allRows.filter((row) => { + if (category !== 'All' && row.category !== category) { + return false; + } + if (!needle) { + return true; + } + return ( + row.parameter.toLowerCase().includes(needle) || row.value.toLowerCase().includes(needle) + ); + }); + }, [allRows, category, search]); + + const categoryOptions = [ + { label: 'All', value: 'All' }, + { label: 'System & Framework', value: 'System & Framework' }, + { label: 'Memory & GC', value: 'Memory & GC' }, + ...(showModules ? [{ label: 'System Property', value: 'System Property' }] : []), + ]; + + const categoryMenu: MenuProps = { + items: categoryOptions.map((o) => ({ key: o.value, label: o.label })), + selectable: true, + selectedKeys: [category], + onClick: ({ key }) => setCategory(key as 'All' | JvmParameterCategory), + }; + + // Copy the selected rows (or all filtered rows when none are selected) as a + // Hadoop-style XML configuration snippet. Selection resolves against the full + // row set so it survives search/category filtering. + const copyArguments = async () => { + const chosen = selectedRowKeys.length + ? allRows.filter((r) => selectedRowKeys.includes(r.key)) + : rows; + if (!chosen.length) { + return; + } + await navigator.clipboard.writeText(buildConfigXml(chosen)); Review Comment: Thanks for pointing this out, I overlooked it. Got an idea from Claude and switched to using a hidden textarea to support copying over unsecure connection. But if that is not allowed it fails ultimately and gives proper error. However since Ozone is mostly used in prod I am hoping there aren't a lot of users having unsecured Ozone deployments. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
