From c4c44532c3d8fb314950180068b36698381269a8 Mon Sep 17 00:00:00 2001 From: Michal Petrov Date: Thu, 30 Jan 2025 18:39:55 +0100 Subject: [PATCH] HAL-2006: add runtime view for Resource Adapters statistics --- .../org/jboss/hal/client/ConsoleModule.java | 12 + .../resourceadapter/AddressTemplates.java | 42 ++++ .../resourceadapter/BasePresenter.java | 135 ++++++++++ .../subsystem/resourceadapter/BaseView.java | 152 +++++++++++ .../resourceadapter/ChildResourceColumn.java | 204 +++++++++++++++ .../ChildResourcePresenter.java | 67 +++++ .../resourceadapter/ChildResourcePreview.java | 204 +++++++++++++++ .../resourceadapter/ChildResourceView.java | 29 +++ .../ResourceAdapterColumn.java | 235 ++++++++++++++++++ .../ResourceAdapterPresenter.java | 65 +++++ .../ResourceAdapterPreview.java | 66 +++++ .../resourceadapter/ResourceAdapterView.java | 29 +++ .../resourceadapter/StatisticsResource.java | 106 ++++++++ .../jboss/hal/core/subsystem/Subsystems.java | 6 +- .../org/jboss/hal/meta/token/NameTokens.java | 2 + .../java/org/jboss/hal/resources/Ids.java | 11 + .../org/jboss/hal/resources/Messages.java | 2 + .../java/org/jboss/hal/resources/Names.java | 1 + .../org/jboss/hal/resources/Previews.java | 3 + .../jboss/hal/resources/Messages.properties | 2 + .../previews/runtime/resource-adapters.html | 1 + 21 files changed, 1373 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/AddressTemplates.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/BasePresenter.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/BaseView.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourceColumn.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourcePresenter.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourcePreview.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourceView.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterColumn.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterPresenter.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterPreview.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterView.java create mode 100644 app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/StatisticsResource.java create mode 100644 resources/src/main/resources/org/jboss/hal/resources/previews/runtime/resource-adapters.html diff --git a/app/src/main/java/org/jboss/hal/client/ConsoleModule.java b/app/src/main/java/org/jboss/hal/client/ConsoleModule.java index b71d51ba38..7e39455284 100644 --- a/app/src/main/java/org/jboss/hal/client/ConsoleModule.java +++ b/app/src/main/java/org/jboss/hal/client/ConsoleModule.java @@ -173,6 +173,8 @@ import org.jboss.hal.client.runtime.subsystem.messaging.JmsQueueView; import org.jboss.hal.client.runtime.subsystem.microprofile.health.MicroProfileHealthPresenter; import org.jboss.hal.client.runtime.subsystem.microprofile.health.MicroProfileHealthView; +import org.jboss.hal.client.runtime.subsystem.resourceadapter.ChildResourcePresenter; +import org.jboss.hal.client.runtime.subsystem.resourceadapter.ChildResourceView; import org.jboss.hal.client.skeleton.FooterPresenter; import org.jboss.hal.client.skeleton.FooterView; import org.jboss.hal.client.skeleton.HeaderPresenter; @@ -600,6 +602,16 @@ protected void configure() { Mbui_ResourceAdapterView.class, ResourceAdapterPresenter.MyProxy.class); + bindPresenter(org.jboss.hal.client.runtime.subsystem.resourceadapter.ResourceAdapterPresenter.class, + org.jboss.hal.client.runtime.subsystem.resourceadapter.ResourceAdapterPresenter.MyView.class, + org.jboss.hal.client.runtime.subsystem.resourceadapter.ResourceAdapterView.class, + org.jboss.hal.client.runtime.subsystem.resourceadapter.ResourceAdapterPresenter.MyProxy.class); + + bindPresenter(ChildResourcePresenter.class, + ChildResourcePresenter.MyView.class, + ChildResourceView.class, + ChildResourcePresenter.MyProxy.class); + bindPresenter(RhcpPresenter.class, RhcpPresenter.MyView.class, RhcpView.class, diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/AddressTemplates.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/AddressTemplates.java new file mode 100644 index 0000000000..317879f7e8 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/AddressTemplates.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import org.jboss.hal.meta.AddressTemplate; + +interface AddressTemplates { + + String EXTENDED_STATISTICS = "/statistics=extended"; + String POOL_STATISTICS = "/statistics=pool"; + + String RESOURCE_ADAPTER_SUBSYSTEM = "/subsystem=resource-adapters"; + String RESOURCE_ADAPTER_SUBSYSTEM_ADDRESS = "/{selected.host}/{selected.server}" + RESOURCE_ADAPTER_SUBSYSTEM; + String RESOURCE_ADAPTER_ADDRESS = RESOURCE_ADAPTER_SUBSYSTEM_ADDRESS + "/resource-adapter=*"; + String RESOURCE_ADAPTER_CONFIGURATION_ADDRESS = RESOURCE_ADAPTER_SUBSYSTEM + "/resource-adapter=*"; + String CONNECTION_DEFINITION_ADDRESS = RESOURCE_ADAPTER_ADDRESS + "/connection-definitions=*"; + String ADMIN_OBJECT_ADDRESS = RESOURCE_ADAPTER_ADDRESS + "/admin-objects=*"; + + String RESOURCE_ADAPTER_STATS_ADDRESS = AddressTemplate.OPTIONAL + "/" + RESOURCE_ADAPTER_ADDRESS + EXTENDED_STATISTICS; + String ADMIN_OBJECT_STATS_ADDRESS = AddressTemplate.OPTIONAL + "/" + ADMIN_OBJECT_ADDRESS + EXTENDED_STATISTICS; + String CONN_DEF_EXT_STATS_ADDRESS = AddressTemplate.OPTIONAL + "/" + CONNECTION_DEFINITION_ADDRESS + EXTENDED_STATISTICS; + String CONN_DEF_POOL_STATS_ADDRESS = CONNECTION_DEFINITION_ADDRESS + POOL_STATISTICS; + + AddressTemplate RESOURCE_ADAPTER_SUBSYSTEM_TEMPLATE = AddressTemplate.of(RESOURCE_ADAPTER_SUBSYSTEM_ADDRESS); + AddressTemplate RESOURCE_ADAPTER_TEMPLATE = AddressTemplate.of(RESOURCE_ADAPTER_ADDRESS); + + AddressTemplate ADMIN_OBJECT_TEMPLATE = AddressTemplate.of(ADMIN_OBJECT_ADDRESS); + AddressTemplate CONN_DEF_TEMPLATE = AddressTemplate.of(CONNECTION_DEFINITION_ADDRESS); +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/BasePresenter.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/BasePresenter.java new file mode 100644 index 0000000000..fec1cf1c0e --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/BasePresenter.java @@ -0,0 +1,135 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import org.jboss.hal.core.finder.Finder; +import org.jboss.hal.core.finder.FinderPath; +import org.jboss.hal.core.finder.FinderPathFactory; +import org.jboss.hal.core.mvp.ApplicationFinderPresenter; +import org.jboss.hal.core.mvp.HalView; +import org.jboss.hal.core.mvp.HasPresenter; +import org.jboss.hal.dmr.Operation; +import org.jboss.hal.dmr.ResourceAddress; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.meta.AddressTemplate; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.resources.Ids; +import org.jboss.hal.resources.Names; +import org.jboss.hal.resources.Resources; + +import com.google.web.bindery.event.shared.EventBus; +import com.gwtplatform.mvp.client.proxy.ProxyPlace; +import com.gwtplatform.mvp.shared.proxy.PlaceRequest; + +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.EXTENDED_STATISTICS; +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.POOL_STATISTICS; +import static org.jboss.hal.dmr.ModelDescriptionConstants.INCLUDE_RUNTIME; +import static org.jboss.hal.dmr.ModelDescriptionConstants.NAME; +import static org.jboss.hal.dmr.ModelDescriptionConstants.READ_RESOURCE_OPERATION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RECURSIVE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RESOURCE_ADAPTERS; +import static org.jboss.hal.dmr.ModelDescriptionConstants.TYPE; + +public abstract class BasePresenter, P extends BasePresenter.MyProxy> + extends ApplicationFinderPresenter { + + private final FinderPathFactory finderPathFactory; + private final Dispatcher dispatcher; + private final StatementContext statementContext; + private final Resources resources; + private String resourceName; + private String parentName; + private StatisticsResource.ResourceType type; + + public BasePresenter(EventBus eventBus, + V view, + P myProxy, + Finder finder, + FinderPathFactory finderPathFactory, + Dispatcher dispatcher, + StatementContext statementContext, + Resources resources) { + super(eventBus, view, myProxy, finder); + this.finderPathFactory = finderPathFactory; + this.dispatcher = dispatcher; + this.statementContext = statementContext; + this.resources = resources; + } + + @Override + public void prepareFromRequest(PlaceRequest request) { + super.prepareFromRequest(request); + parentName = request.getParameter("parent", null); + resourceName = request.getParameter(NAME, null); + type = StatisticsResource.ResourceType.valueOf(request.getParameter(TYPE, null)); + getView().setup(); + } + + @Override + public FinderPath finderPath() { + FinderPath path = finderPathFactory.runtimeServerPath() + .append(Ids.RUNTIME_SUBSYSTEM, RESOURCE_ADAPTERS, resources.constants().monitor(), + Names.RESOURCE_ADAPTERS); + + String raName = type == StatisticsResource.ResourceType.RESOURCE_ADAPTER ? resourceName : parentName; + path.append(Ids.RESOURCE_ADAPTER_RUNTIME, Ids.resourceAdapterRuntime(raName), Names.RESOURCE_ADAPTER, raName); + + if (type != StatisticsResource.ResourceType.RESOURCE_ADAPTER) { + path.append(Ids.RESOURCE_ADAPTER_CHILD_RUNTIME, Ids.resourceAdapterChildRuntime(parentName, resourceName), + Names.RESOURCE_ADAPTER + " Child", resourceName); + } + return path; + } + + @Override + protected void reload() { + ResourceAddress address = getResourceAddress().resolve(statementContext); + Operation operation = new Operation.Builder(address, READ_RESOURCE_OPERATION) + .param(INCLUDE_RUNTIME, true) + .param(RECURSIVE, true) + .build(); + dispatcher.execute(operation, + result -> getView().update(new StatisticsResource(parentName, resourceName, type, result))); + } + + public StatisticsResource.ResourceType getType() { + return type; + } + + public AddressTemplate getResourceAddress() { + AddressTemplate template = type.getTemplate(); + return type == StatisticsResource.ResourceType.RESOURCE_ADAPTER + ? template.replaceWildcards(resourceName) + : template.replaceWildcards(parentName, resourceName); + } + + public AddressTemplate getExtendedStatsAddress() { + return getResourceAddress().append(EXTENDED_STATISTICS); + } + + public AddressTemplate getPoolStatsAddress() { + return getResourceAddress().append(POOL_STATISTICS); + } + + public interface MyProxy

> extends ProxyPlace

{ + } + + public interface MyView

> extends HalView, HasPresenter

{ + void setup(); + + void update(StatisticsResource resource); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/BaseView.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/BaseView.java new file mode 100644 index 0000000000..407f1dcbb7 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/BaseView.java @@ -0,0 +1,152 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import javax.inject.Inject; + +import org.jboss.hal.ballroom.Alert; +import org.jboss.hal.ballroom.Tabs; +import org.jboss.hal.ballroom.form.Form; +import org.jboss.hal.core.mbui.form.ModelNodeForm; +import org.jboss.hal.core.mvp.HalViewImpl; +import org.jboss.hal.dmr.ModelNode; +import org.jboss.hal.meta.Metadata; +import org.jboss.hal.meta.MetadataRegistry; +import org.jboss.hal.meta.MissingMetadataException; +import org.jboss.hal.resources.Icons; +import org.jboss.hal.resources.Ids; +import org.jboss.hal.resources.Names; +import org.jboss.hal.resources.Resources; + +import elemental2.dom.HTMLElement; + +import static org.jboss.elemento.Elements.a; +import static org.jboss.elemento.Elements.h; +import static org.jboss.elemento.Elements.p; +import static org.jboss.elemento.Elements.span; +import static org.jboss.elemento.EventType.click; +import static org.jboss.hal.ballroom.LayoutBuilder.column; +import static org.jboss.hal.ballroom.LayoutBuilder.row; +import static org.jboss.hal.dmr.ModelDescriptionConstants.STATISTICS_ENABLED; +import static org.jboss.hal.resources.CSS.clearfix; +import static org.jboss.hal.resources.CSS.clickable; +import static org.jboss.hal.resources.CSS.fontAwesome; +import static org.jboss.hal.resources.CSS.marginRight5; +import static org.jboss.hal.resources.CSS.pullRight; + +public abstract class BaseView

> extends HalViewImpl + implements BasePresenter.MyView

{ + + private final MetadataRegistry metadataRegistry; + private final Resources resources; + private P presenter; + private HTMLElement header; + private Form poolForm; + private Form extendedForm; + + @Inject + public BaseView(MetadataRegistry metadataRegistry, Resources resources) { + this.metadataRegistry = metadataRegistry; + this.resources = resources; + } + + @Override + public void setup() { + // The metadata for the "statistic" resources is only available for existing resource-adapters. + // That's why we cannot set up the UI in the constructor like in other views and + // using wildcards in the address templates. As a workaround we defer the UI setup + // until the RA name is known and replace the wildcards with the RA name. + Metadata poolMeta; + Metadata extendedMeta = null; + try { + extendedMeta = metadataRegistry.lookup(presenter.getExtendedStatsAddress()); + } catch (MissingMetadataException mme) { + // "extended" statistics are only present if the underlying class implements them + } + + boolean isConnDef = presenter.getType() == StatisticsResource.ResourceType.CONNECTION_DEFINITION; + HTMLElement extendedElement; + HTMLElement statsElement; + if (extendedMeta != null) { + extendedForm = new ModelNodeForm.Builder<>(Ids.build(Ids.RESOURCE_ADAPTER_RUNTIME, "extended", Ids.FORM), + extendedMeta) + .readOnly() + .includeRuntime() + .exclude(STATISTICS_ENABLED) + .build(); + extendedElement = extendedForm.element(); + + registerAttachable(extendedForm); + } else { + extendedElement = new Alert(Icons.INFO, resources.messages().noStatistics()).element(); + } + + if (!isConnDef) { + statsElement = extendedElement; + } else { + poolMeta = metadataRegistry.lookup(presenter.getPoolStatsAddress()); + poolForm = new ModelNodeForm.Builder<>(Ids.build(Ids.RESOURCE_ADAPTER_CHILD_RUNTIME, Ids.POOL, Ids.FORM), poolMeta) + .readOnly() + .includeRuntime() + .exclude(STATISTICS_ENABLED) + .build(); + + Tabs tabs = new Tabs(Ids.RESOURCE_ADAPTER_CHILD_RUNTIME_TAB_CONTAINER); + tabs.add(Ids.build(Ids.RESOURCE_ADAPTER_CHILD_RUNTIME, "pool", Ids.TAB), Names.POOL, poolForm.element()); + tabs.add(Ids.build(Ids.RESOURCE_ADAPTER_CHILD_RUNTIME, "extended", Ids.TAB), "Extended", extendedElement); + + statsElement = tabs.element(); + + registerAttachable(poolForm); + } + + HTMLElement root = row() + .add(column() + .add(header = h(1).textContent("RA resource").element()) + .add(extendedMeta != null ? p().css(clearfix) + .add(a().css(clickable, pullRight).on(click, event -> refresh()) + .add(span().css(fontAwesome("refresh"), marginRight5)) + .add(span().textContent(resources.constants().refresh()))) + : null) + .add(statsElement)) + .element(); + + initElement(root); + } + + @Override + public void setPresenter(P presenter) { + this.presenter = presenter; + } + + @Override + public void update(StatisticsResource resource) { + header.textContent = resource.getName(); + + if (resource.hasExtendedStats()) { + extendedForm.view(resource.getExtendedStats()); + } + if (presenter.getType() == StatisticsResource.ResourceType.CONNECTION_DEFINITION) { + poolForm.view(resource.getPoolStats()); + } + } + + private void refresh() { + if (presenter != null) { + presenter.reload(); + } + } +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourceColumn.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourceColumn.java new file mode 100644 index 0000000000..c3e58a5b2e --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourceColumn.java @@ -0,0 +1,204 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.inject.Inject; + +import org.jboss.hal.config.Environment; +import org.jboss.hal.core.finder.Finder; +import org.jboss.hal.core.finder.FinderColumn; +import org.jboss.hal.core.finder.FinderSegment; +import org.jboss.hal.core.finder.ItemAction; +import org.jboss.hal.core.finder.ItemActionFactory; +import org.jboss.hal.core.finder.ItemDisplay; +import org.jboss.hal.core.finder.ItemsProvider; +import org.jboss.hal.core.mvp.Places; +import org.jboss.hal.core.runtime.server.Server; +import org.jboss.hal.core.runtime.server.ServerActions; +import org.jboss.hal.dmr.Composite; +import org.jboss.hal.dmr.NamedNode; +import org.jboss.hal.dmr.Operation; +import org.jboss.hal.dmr.ResourceAddress; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.meta.AddressTemplate; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.meta.token.NameTokens; +import org.jboss.hal.resources.Icons; +import org.jboss.hal.resources.Ids; +import org.jboss.hal.resources.Names; +import org.jboss.hal.resources.Resources; +import org.jboss.hal.spi.AsyncColumn; +import org.jboss.hal.spi.Requires; + +import com.gwtplatform.mvp.shared.proxy.PlaceRequest; + +import elemental2.dom.HTMLElement; +import elemental2.promise.Promise; + +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.toList; + +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.ADMIN_OBJECT_ADDRESS; +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.CONNECTION_DEFINITION_ADDRESS; +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.RESOURCE_ADAPTER_TEMPLATE; +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.StatisticsResource.ResourceType.ADMIN_OBJECT; +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.StatisticsResource.ResourceType.CONNECTION_DEFINITION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.ADMIN_OBJECTS; +import static org.jboss.hal.dmr.ModelDescriptionConstants.ATTRIBUTES_ONLY; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CHILD_TYPE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CONNECTION_DEFINITIONS; +import static org.jboss.hal.dmr.ModelDescriptionConstants.INCLUDE_RUNTIME; +import static org.jboss.hal.dmr.ModelDescriptionConstants.NAME; +import static org.jboss.hal.dmr.ModelDescriptionConstants.READ_CHILDREN_RESOURCES_OPERATION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.READ_RESOURCE_OPERATION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RECURSIVE_DEPTH; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RESULT; +import static org.jboss.hal.dmr.ModelDescriptionConstants.TYPE; +import static org.jboss.hal.meta.StatementContext.Expression.SELECTED_HOST; +import static org.jboss.hal.meta.StatementContext.Expression.SELECTED_SERVER; + +@AsyncColumn(Ids.RESOURCE_ADAPTER_CHILD_RUNTIME) +@Requires({ ADMIN_OBJECT_ADDRESS, CONNECTION_DEFINITION_ADDRESS }) +public class ChildResourceColumn extends FinderColumn { + + private Server server; + + @Inject + public ChildResourceColumn(ServerActions serverActions, + Dispatcher dispatcher, + StatementContext statementContext, + Environment environment, + Resources resources, + Finder finder, + ItemActionFactory itemActionFactory, + Places places) { + + super(new Builder(finder, Ids.RESOURCE_ADAPTER_CHILD_RUNTIME, Names.RESOURCE_ADAPTER + " Child") + .withFilter() + .filterDescription(resources.messages().filterBy("name, type")) + .useFirstActionAsBreadcrumbHandler()); + + ItemsProvider itemsProvider = context -> { + // extract server name from the finder path + FinderSegment segment = context.getPath().findColumn(Ids.RESOURCE_ADAPTER_RUNTIME); + + if (segment != null) { + String raName = segment.getItemTitle(); + List operations = new ArrayList<>(); + + ResourceAddress resourceAdapterAddress = RESOURCE_ADAPTER_TEMPLATE.resolve(statementContext, raName); + operations.add(new Operation.Builder(resourceAdapterAddress, READ_CHILDREN_RESOURCES_OPERATION) + .param(CHILD_TYPE, ADMIN_OBJECTS) + .param(INCLUDE_RUNTIME, true) + .param(RECURSIVE_DEPTH, 2) + .build()); + + operations.add(new Operation.Builder(resourceAdapterAddress, READ_CHILDREN_RESOURCES_OPERATION) + .param(CHILD_TYPE, CONNECTION_DEFINITIONS) + .param(INCLUDE_RUNTIME, true) + .param(RECURSIVE_DEPTH, 2) + .build()); + + if (!environment.isStandalone()) { + ResourceAddress serverAddress = AddressTemplate.of(SELECTED_HOST, SELECTED_SERVER) + .resolve(statementContext); + operations.add(new Operation.Builder(serverAddress, READ_RESOURCE_OPERATION) + .param(INCLUDE_RUNTIME, true) + .param(ATTRIBUTES_ONLY, true) + .build()); + } + return dispatcher.execute(new Composite(operations)).then(result -> { + server = environment.isStandalone() + ? Server.STANDALONE + : new Server(statementContext.selectedHost(), result.step(2).get(RESULT)); + List combined = new ArrayList<>(); + combined.addAll(result.step(0).get(RESULT).asPropertyList().stream() + .map(ao -> new StatisticsResource(raName, ADMIN_OBJECT, ao)).collect(toList())); + combined.addAll(result.step(1).get(RESULT).asPropertyList().stream() + .map(cd -> new StatisticsResource(raName, CONNECTION_DEFINITION, cd)).collect(toList())); + combined.sort(comparing(NamedNode::getName)); + return Promise.resolve(combined); + }); + } + return Promise.resolve(Collections.emptyList()); + }; + setItemsProvider(itemsProvider); + + // reuse the items provider to filter breadcrumb items + setBreadcrumbItemsProvider(context -> itemsProvider.items(context) + .then(result -> Promise.resolve(result.stream() + .filter(StatisticsResource::isStatisticsEnabled) + .collect(toList())))); + + setItemRenderer(raChild -> new ItemDisplay() { + @Override + public String getId() { + return Ids.resourceAdapterChildRuntime(raChild.getParentName(), raChild.getName()); + } + + @Override + public HTMLElement element() { + String subtitle = raChild.getResourceType().name().toLowerCase().replace('_', '-'); + return ItemDisplay.withSubtitle(raChild.getName(), subtitle); + } + + @Override + public String getTitle() { + return raChild.getName(); + } + + @Override + public HTMLElement getIcon() { + return raChild.isStatisticsEnabled() ? Icons.ok() : Icons.unknown(); + } + + @Override + public String getTooltip() { + return raChild.isStatisticsEnabled() ? resources.constants().enabled() + : resources.constants().statisticsDisabled(); + } + + @Override + public String getFilterData() { + // noinspection HardCodedStringLiteral + return getTitle() + " " + raChild.getResourceType().name().toLowerCase().replace('_', '-'); + } + + @Override + @SuppressWarnings("HardCodedStringLiteral") + public List> actions() { + List> actions = new ArrayList<>(); + + PlaceRequest placeRequest = places.selectedServer(NameTokens.RESOURCE_ADAPTER_CHILD_RUNTIME) + .with("parent", raChild.getParentName()) + .with(NAME, raChild.getName()) + .with(TYPE, raChild.getResourceType().name()) + .build(); + actions.add(itemActionFactory.view(placeRequest)); + + return actions; + } + }); + + setPreviewCallback(child -> new ChildResourcePreview(server, child, environment, dispatcher, statementContext, + serverActions, resources)); + } + +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourcePresenter.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourcePresenter.java new file mode 100644 index 0000000000..94ffe8add4 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourcePresenter.java @@ -0,0 +1,67 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import javax.inject.Inject; + +import org.jboss.hal.core.finder.Finder; +import org.jboss.hal.core.finder.FinderPathFactory; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.resources.Resources; +import org.jboss.hal.spi.Requires; + +import com.google.web.bindery.event.shared.EventBus; +import com.gwtplatform.mvp.client.annotations.NameToken; +import com.gwtplatform.mvp.client.annotations.ProxyCodeSplit; + +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.ADMIN_OBJECT_STATS_ADDRESS; +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.CONN_DEF_EXT_STATS_ADDRESS; +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.CONN_DEF_POOL_STATS_ADDRESS; +import static org.jboss.hal.meta.token.NameTokens.RESOURCE_ADAPTER_CHILD_RUNTIME; + +public class ChildResourcePresenter + extends BasePresenter { + + @Inject + public ChildResourcePresenter(EventBus eventBus, + MyView view, + MyProxy myProxy, + Finder finder, + FinderPathFactory finderPathFactory, + Dispatcher dispatcher, + StatementContext statementContext, + Resources resources) { + super(eventBus, view, myProxy, finder, finderPathFactory, dispatcher, statementContext, resources); + } + + @Override + protected void onBind() { + super.onBind(); + getView().setPresenter(this); + } + + // @formatter:off + @ProxyCodeSplit + @NameToken(RESOURCE_ADAPTER_CHILD_RUNTIME) + @Requires({ ADMIN_OBJECT_STATS_ADDRESS, CONN_DEF_EXT_STATS_ADDRESS, CONN_DEF_POOL_STATS_ADDRESS}) + public interface MyProxy extends BasePresenter.MyProxy { + } + + public interface MyView extends BasePresenter.MyView { + } + // @formatter:on +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourcePreview.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourcePreview.java new file mode 100644 index 0000000000..fbeae28523 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourcePreview.java @@ -0,0 +1,204 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.elemento.Elements; +import org.jboss.hal.ballroom.Alert; +import org.jboss.hal.ballroom.chart.Utilization; +import org.jboss.hal.config.Environment; +import org.jboss.hal.core.finder.PreviewAttributes; +import org.jboss.hal.core.finder.PreviewContent; +import org.jboss.hal.core.runtime.server.Server; +import org.jboss.hal.core.runtime.server.ServerActions; +import org.jboss.hal.dmr.Composite; +import org.jboss.hal.dmr.CompositeResult; +import org.jboss.hal.dmr.ModelNode; +import org.jboss.hal.dmr.Operation; +import org.jboss.hal.dmr.ResourceAddress; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.meta.AddressTemplate; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.meta.security.Constraint; +import org.jboss.hal.resources.Icons; +import org.jboss.hal.resources.Names; +import org.jboss.hal.resources.Resources; + +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; + +import elemental2.dom.HTMLElement; + +import static org.jboss.elemento.Elements.h; +import static org.jboss.elemento.Elements.setVisible; +import static org.jboss.hal.dmr.ModelDescriptionConstants.ATTRIBUTES_ONLY; +import static org.jboss.hal.dmr.ModelDescriptionConstants.INCLUDE_RUNTIME; +import static org.jboss.hal.dmr.ModelDescriptionConstants.READ_RESOURCE_OPERATION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RECURSIVE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RELOAD; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RESTART; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RESULT; +import static org.jboss.hal.meta.StatementContext.Expression.SELECTED_HOST; +import static org.jboss.hal.meta.StatementContext.Expression.SELECTED_SERVER; +import static org.jboss.hal.resources.CSS.hidden; +import static org.jboss.hal.resources.CSS.underline; + +class ChildResourcePreview extends PreviewContent { + private final Server server; + private final StatisticsResource raChild; + private final Environment environment; + private final Dispatcher dispatcher; + private final StatementContext statementContext; + private final ResourceAddress resourceAddress; + + private Alert needsReloadWarning; + private Alert needsRestartWarning; + private HTMLElement refresh; + private HTMLElement poolHeader; + private Utilization activeConnections; + private Utilization maxUsedConnections; + + ChildResourcePreview(Server server, + StatisticsResource raChild, + Environment environment, + Dispatcher dispatcher, + StatementContext statementContext, + ServerActions serverActions, + Resources resources) { + + super(raChild.getName(), raChild.getResourceType().name()); + this.server = server; + this.raChild = raChild; + this.environment = environment; + this.dispatcher = dispatcher; + this.statementContext = statementContext; + this.resourceAddress = raChild.getResourceType().getTemplate().resolve(statementContext, raChild.getParentName(), + raChild.getName()); + + raChild.get(StatisticsResource.EXT_STATS_AVAILABLE).set(raChild.getExtendedStats().isDefined()); + previewBuilder() + .addAll(new PreviewAttributes<>(raChild, List.of(StatisticsResource.EXT_STATS_AVAILABLE))); + + if (raChild.getResourceType() == StatisticsResource.ResourceType.ADMIN_OBJECT) { + return; + } + + Constraint reloadConstraint = environment.isStandalone() + ? Constraint.executable(AddressTemplate.of("/"), RELOAD) + : Constraint.executable(AddressTemplate.of("/{selected.host}/server-config=*"), RELOAD); + Constraint restartConstraint = environment.isStandalone() + ? Constraint.executable(AddressTemplate.of("/"), RELOAD) + : Constraint.executable(AddressTemplate.of("/{selected.host}/server-config=*"), RESTART); + + needsReloadWarning = new Alert(Icons.WARNING, + new SafeHtmlBuilder() + .append(resources.messages().serverNeedsReload(server.getName())) + .appendEscaped(" ") + .append(resources.messages().staleStatistics()) + .toSafeHtml(), + resources.constants().reload(), event -> serverActions.reload(server), + reloadConstraint); + + needsRestartWarning = new Alert(Icons.WARNING, + new SafeHtmlBuilder() + .append(resources.messages().serverNeedsRestart(server.getName())) + .appendEscaped(" ") + .append(resources.messages().staleStatistics()) + .toSafeHtml(), + resources.constants().restart(), event -> serverActions.restart(server), + restartConstraint); + + activeConnections = new Utilization(resources.constants().active(), Names.CONNECTIONS, + environment.isStandalone(), true); + maxUsedConnections = new Utilization(resources.constants().maxUsed(), Names.CONNECTIONS, + environment.isStandalone(), true); + getHeaderContainer().appendChild(refresh = refreshLink(() -> update(null))); + previewBuilder() + .add(needsReloadWarning) + .add(needsRestartWarning) + .add(poolHeader = h(2).css(underline).textContent(Names.CONNECTION_POOL).element()) + .add(activeConnections) + .add(maxUsedConnections); + + // to prevent flickering we initially hide everything + needsReloadWarning.element().classList.add(hidden); + needsRestartWarning.element().classList.add(hidden); + } + + @Override + @SuppressWarnings("HardCodedStringLiteral") + public void update(StatisticsResource sr) { + if (raChild.getResourceType() == StatisticsResource.ResourceType.ADMIN_OBJECT) { + return; + } + List operations = new ArrayList<>(); + if (environment.isStandalone()) { + operations.add(new Operation.Builder(ResourceAddress.root(), READ_RESOURCE_OPERATION) + .param(INCLUDE_RUNTIME, true) + .param(ATTRIBUTES_ONLY, true) + .build()); + } else { + ResourceAddress address = AddressTemplate.of(SELECTED_HOST, SELECTED_SERVER) + .resolve(statementContext); + operations.add(new Operation.Builder(address, READ_RESOURCE_OPERATION) + .param(INCLUDE_RUNTIME, true) + .param(ATTRIBUTES_ONLY, true) + .build()); + } + if (sr == null) { + operations.add(new Operation.Builder(resourceAddress, READ_RESOURCE_OPERATION) + .param(INCLUDE_RUNTIME, true) + .param(RECURSIVE, true) + .build()); + } + + dispatcher.execute(new Composite(operations), (CompositeResult result) -> { + server.addServerAttributes(result.step(0).get(RESULT)); + if (sr == null) { + raChild.update(result.step(1).get(RESULT)); + } + + boolean statisticsEnabled = raChild.isStatisticsEnabled(); + setVisible(refresh, statisticsEnabled); + setVisible(poolHeader, statisticsEnabled); + setVisible(activeConnections.element(), statisticsEnabled); + setVisible(maxUsedConnections.element(), statisticsEnabled); + + // Do not simply hide the links, but add the hidden CSS class. + // Important when constraints for the links are processed later. + needsReloadWarning.element().classList.add(hidden); + needsRestartWarning.element().classList.add(hidden); + if (statisticsEnabled) { + Elements.toggle(needsReloadWarning.element(), hidden, !server.needsReload()); + Elements.toggle(needsRestartWarning.element(), hidden, !server.needsRestart()); + + // pool statistics + ModelNode pool = raChild.getPoolStats(); + if (pool.isDefined()) { + int available = pool.get("AvailableCount").asInt(0); + int active = pool.get("ActiveCount").asInt(0); + int maxUsed = pool.get("MaxUsedCount").asInt(0); + activeConnections.update(active, available); + maxUsedConnections.update(maxUsed, available); + } else { + activeConnections.update(0, 0); + maxUsedConnections.update(0, 0); + } + } + }); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourceView.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourceView.java new file mode 100644 index 0000000000..691c47f36c --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ChildResourceView.java @@ -0,0 +1,29 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import javax.inject.Inject; + +import org.jboss.hal.meta.MetadataRegistry; +import org.jboss.hal.resources.Resources; + +public class ChildResourceView extends BaseView implements ChildResourcePresenter.MyView { + + @Inject + public ChildResourceView(MetadataRegistry metadataRegistry, Resources resources) { + super(metadataRegistry, resources); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterColumn.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterColumn.java new file mode 100644 index 0000000000..eebce6f5fb --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterColumn.java @@ -0,0 +1,235 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.jboss.hal.config.Environment; +import org.jboss.hal.core.finder.Finder; +import org.jboss.hal.core.finder.FinderColumn; +import org.jboss.hal.core.finder.ItemAction; +import org.jboss.hal.core.finder.ItemActionFactory; +import org.jboss.hal.core.finder.ItemDisplay; +import org.jboss.hal.core.finder.ItemsProvider; +import org.jboss.hal.core.mvp.Places; +import org.jboss.hal.core.runtime.server.Server; +import org.jboss.hal.dmr.Composite; +import org.jboss.hal.dmr.Operation; +import org.jboss.hal.dmr.ResourceAddress; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.meta.AddressTemplate; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.meta.security.Constraint; +import org.jboss.hal.meta.token.NameTokens; +import org.jboss.hal.resources.Icons; +import org.jboss.hal.resources.Ids; +import org.jboss.hal.resources.Names; +import org.jboss.hal.resources.Resources; +import org.jboss.hal.spi.AsyncColumn; +import org.jboss.hal.spi.Message; +import org.jboss.hal.spi.MessageEvent; +import org.jboss.hal.spi.Requires; + +import com.google.web.bindery.event.shared.EventBus; +import com.gwtplatform.mvp.shared.proxy.PlaceRequest; + +import elemental2.dom.HTMLElement; +import elemental2.promise.Promise; + +import static java.util.stream.Collectors.toList; + +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.*; +import static org.jboss.hal.core.finder.FinderColumn.RefreshMode.RESTORE_SELECTION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.ATTRIBUTES_ONLY; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CHILD_TYPE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.INCLUDE_RUNTIME; +import static org.jboss.hal.dmr.ModelDescriptionConstants.NAME; +import static org.jboss.hal.dmr.ModelDescriptionConstants.PROFILE_NAME; +import static org.jboss.hal.dmr.ModelDescriptionConstants.READ_CHILDREN_RESOURCES_OPERATION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.READ_RESOURCE_OPERATION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RESOURCE_ADAPTER; +import static org.jboss.hal.dmr.ModelDescriptionConstants.RESULT; +import static org.jboss.hal.dmr.ModelDescriptionConstants.STATISTICS_ENABLED; +import static org.jboss.hal.dmr.ModelDescriptionConstants.TYPE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.VALUE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; +import static org.jboss.hal.meta.StatementContext.Expression.SELECTED_HOST; +import static org.jboss.hal.meta.StatementContext.Expression.SELECTED_SERVER; + +@AsyncColumn(Ids.RESOURCE_ADAPTER_RUNTIME) +@Requires({ RESOURCE_ADAPTER_ADDRESS }) +public class ResourceAdapterColumn extends FinderColumn { + + private final Dispatcher dispatcher; + private final EventBus eventBus; + private final StatementContext statementContext; + private final Environment environment; + private final Resources resources; + private final Finder finder; + private Server server; + + @Inject + public ResourceAdapterColumn(Dispatcher dispatcher, + EventBus eventBus, + StatementContext statementContext, + Environment environment, + Resources resources, + Finder finder, + ItemActionFactory itemActionFactory, + Places places) { + + super(new Builder(finder, Ids.RESOURCE_ADAPTER_RUNTIME, Names.RESOURCE_ADAPTER) + .withFilter() + .useFirstActionAsBreadcrumbHandler()); + + this.dispatcher = dispatcher; + this.eventBus = eventBus; + this.statementContext = statementContext; + this.environment = environment; + this.resources = resources; + this.finder = finder; + + ItemsProvider itemsProvider = context -> { + List operations = new ArrayList<>(); + + ResourceAddress raSubsystemAddress = RESOURCE_ADAPTER_SUBSYSTEM_TEMPLATE.resolve(statementContext); + operations.add(new Operation.Builder(raSubsystemAddress, READ_CHILDREN_RESOURCES_OPERATION) + .param(CHILD_TYPE, RESOURCE_ADAPTER) + .param(INCLUDE_RUNTIME, true) + .build()); + + if (!environment.isStandalone()) { + ResourceAddress serverAddress = AddressTemplate.of(SELECTED_HOST, SELECTED_SERVER) + .resolve(statementContext); + operations.add(new Operation.Builder(serverAddress, READ_RESOURCE_OPERATION) + .param(INCLUDE_RUNTIME, true) + .param(ATTRIBUTES_ONLY, true) + .build()); + } + return dispatcher.execute(new Composite(operations)).then(result -> { + server = environment.isStandalone() + ? Server.STANDALONE + : new Server(statementContext.selectedHost(), result.step(1).get(RESULT)); + return Promise.resolve(result.step(0).get(RESULT).asPropertyList().stream() + .map(StatisticsResource::new) + .collect(toList())); + }); + }; + setItemsProvider(itemsProvider); + + // reuse the items provider to filter breadcrumb items + setBreadcrumbItemsProvider(context -> itemsProvider.items(context) + .then(result -> Promise.resolve(result.stream() + .filter(ra -> ra.get(STATISTICS_ENABLED).asBoolean()) + .collect(toList())))); + + setItemRenderer(ra -> new ItemDisplay() { + @Override + public String getId() { + return Ids.resourceAdapterRuntime(ra.getName()); + } + + @Override + public String getTitle() { + return ra.getName(); + } + + @Override + public HTMLElement getIcon() { + if (!ra.get(STATISTICS_ENABLED).asBoolean()) { + return Icons.unknown(); + } else { + return Icons.ok(); + } + } + + @Override + public String getTooltip() { + if (!ra.get(STATISTICS_ENABLED).asBoolean()) { + return resources.constants().statisticsDisabled(); + } else { + return resources.constants().enabled(); + } + } + + @Override + public String getFilterData() { + return getTitle(); + } + + @Override + public String nextColumn() { + return Ids.RESOURCE_ADAPTER_CHILD_RUNTIME; + } + + @Override + @SuppressWarnings("HardCodedStringLiteral") + public List> actions() { + List> actions = new ArrayList<>(); + + PlaceRequest placeRequest = places.selectedServer(NameTokens.RESOURCE_ADAPTER_RUNTIME) + .with(NAME, ra.getName()) + .with(TYPE, ra.getResourceType().name()) + .build(); + actions.add(itemActionFactory.view(placeRequest)); + actions.add(new ItemAction.Builder().title("Activate") + .handler(item -> activate(item)) + .constraint(Constraint.executable(RESOURCE_ADAPTER_TEMPLATE, "activate")) + .build()); + + return actions; + } + }); + + setPreviewCallback(item -> new ResourceAdapterPreview(this, item, resources)); + } + + private void activate(StatisticsResource resourceAdapter) { + Operation operation = new Operation.Builder( + RESOURCE_ADAPTER_TEMPLATE.resolve(statementContext, resourceAdapter.getName()), "activate").build(); + dispatcher.execute(operation, + result -> { + refresh(RESTORE_SELECTION); + MessageEvent.fire(eventBus, Message.success(resources.messages().activationSuccess())); + }); + } + + private ResourceAddress resourceAdapterConfigurationAddress(StatisticsResource resourceAdapter) { + if (environment.isStandalone()) { + return AddressTemplate.of(RESOURCE_ADAPTER_CONFIGURATION_ADDRESS).resolve(statementContext, + resourceAdapter.getName()); + } else { + String profile = server.get(PROFILE_NAME).asString(); + return AddressTemplate.of("/profile=*" + RESOURCE_ADAPTER_CONFIGURATION_ADDRESS) + .resolve(statementContext, profile, resourceAdapter.getName()); + } + } + + void enableStatistics(StatisticsResource resourceAdapter) { + + ResourceAddress address = resourceAdapterConfigurationAddress(resourceAdapter); + Operation operation = new Operation.Builder(address, WRITE_ATTRIBUTE_OPERATION).param(NAME, STATISTICS_ENABLED) + .param(VALUE, true).build(); + dispatcher.execute(operation, result -> { + MessageEvent.fire(eventBus, + Message.success(resources.messages().statisticsEnabled(resourceAdapter.getName()))); + finder.refresh(); + }); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterPresenter.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterPresenter.java new file mode 100644 index 0000000000..fbfda367d8 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterPresenter.java @@ -0,0 +1,65 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import javax.inject.Inject; + +import org.jboss.hal.core.finder.Finder; +import org.jboss.hal.core.finder.FinderPathFactory; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.resources.Resources; +import org.jboss.hal.spi.Requires; + +import com.google.web.bindery.event.shared.EventBus; +import com.gwtplatform.mvp.client.annotations.NameToken; +import com.gwtplatform.mvp.client.annotations.ProxyCodeSplit; + +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.RESOURCE_ADAPTER_STATS_ADDRESS; +import static org.jboss.hal.meta.token.NameTokens.RESOURCE_ADAPTER_RUNTIME; + +public class ResourceAdapterPresenter + extends BasePresenter { + + @Inject + public ResourceAdapterPresenter(EventBus eventBus, + MyView view, + MyProxy myProxy, + Finder finder, + FinderPathFactory finderPathFactory, + Dispatcher dispatcher, + StatementContext statementContext, + Resources resources) { + super(eventBus, view, myProxy, finder, finderPathFactory, dispatcher, statementContext, resources); + } + + @Override + protected void onBind() { + super.onBind(); + getView().setPresenter(this); + } + + // @formatter:off + @ProxyCodeSplit + @NameToken(RESOURCE_ADAPTER_RUNTIME) + @Requires({ RESOURCE_ADAPTER_STATS_ADDRESS }) + public interface MyProxy extends BasePresenter.MyProxy { + } + + public interface MyView extends BasePresenter.MyView { + } + // @formatter:on +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterPreview.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterPreview.java new file mode 100644 index 0000000000..be5a248fb0 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterPreview.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import java.util.List; + +import org.jboss.elemento.Elements; +import org.jboss.hal.ballroom.EmptyState; +import org.jboss.hal.core.finder.PreviewAttributes; +import org.jboss.hal.core.finder.PreviewContent; +import org.jboss.hal.meta.security.Constraint; +import org.jboss.hal.resources.Ids; +import org.jboss.hal.resources.Names; +import org.jboss.hal.resources.Resources; + +import static org.jboss.hal.client.runtime.subsystem.transaction.AddressTemplates.TRANSACTION_CONFIGURATION_TEMPLATE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.DISABLED; +import static org.jboss.hal.dmr.ModelDescriptionConstants.STATISTICS; +import static org.jboss.hal.dmr.ModelDescriptionConstants.STATISTICS_ENABLED; +import static org.jboss.hal.resources.CSS.fontAwesome; + +class ResourceAdapterPreview extends PreviewContent { + + private EmptyState noStatistics; + + ResourceAdapterPreview(ResourceAdapterColumn column, StatisticsResource resourceAdapter, Resources resources) { + super(resourceAdapter.getName()); + + noStatistics = new EmptyState.Builder(Ids.build(Ids.RESOURCE_ADAPTER, STATISTICS, DISABLED), + resources.constants().statisticsDisabledHeader()) + .description(resources.messages().statisticsDisabled(Names.RESOURCE_ADAPTER)) + .icon(fontAwesome("line-chart")) + .primaryAction(resources.constants().enableStatistics(), () -> column.enableStatistics(resourceAdapter), + Constraint.writable(TRANSACTION_CONFIGURATION_TEMPLATE, STATISTICS_ENABLED)) + .build(); + Elements.setVisible(noStatistics.element(), false); + + resourceAdapter.get(StatisticsResource.EXT_STATS_AVAILABLE).set(resourceAdapter.hasExtendedStats()); + + previewBuilder() + .add(noStatistics) + .addAll(new PreviewAttributes<>(resourceAdapter, + List.of(StatisticsResource.EXT_STATS_AVAILABLE))); + } + + @Override + public void update(StatisticsResource item) { + super.update(item); + + boolean statsEnabled = item.get(STATISTICS_ENABLED).asBoolean(); + Elements.setVisible(noStatistics.element(), !statsEnabled); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterView.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterView.java new file mode 100644 index 0000000000..ca149f7e0c --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/ResourceAdapterView.java @@ -0,0 +1,29 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import javax.inject.Inject; + +import org.jboss.hal.meta.MetadataRegistry; +import org.jboss.hal.resources.Resources; + +public class ResourceAdapterView extends BaseView implements ResourceAdapterPresenter.MyView { + + @Inject + public ResourceAdapterView(MetadataRegistry metadataRegistry, Resources resources) { + super(metadataRegistry, resources); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/StatisticsResource.java b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/StatisticsResource.java new file mode 100644 index 0000000000..1434a3f970 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/runtime/subsystem/resourceadapter/StatisticsResource.java @@ -0,0 +1,106 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed 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 + * + * https://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. + */ +package org.jboss.hal.client.runtime.subsystem.resourceadapter; + +import org.jboss.hal.dmr.ModelNode; +import org.jboss.hal.dmr.NamedNode; +import org.jboss.hal.dmr.Property; +import org.jboss.hal.meta.AddressTemplate; + +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.ADMIN_OBJECT_TEMPLATE; +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.CONN_DEF_TEMPLATE; +import static org.jboss.hal.client.runtime.subsystem.resourceadapter.AddressTemplates.RESOURCE_ADAPTER_TEMPLATE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.STATISTICS; +import static org.jboss.hal.dmr.ModelDescriptionConstants.STATISTICS_ENABLED; +import static org.jboss.hal.dmr.ModelNodeHelper.failSafeGet; + +/** + * Wrapper for easier handling of "statistics" resources ("statistics" is the only runtime resource) + * + * "statistics=extended" is a child of "resource-adapter=*" and its children "admin-objects=*" and "connection-definitions=*" + * (conditionally present in all) + * + * "statistics=pool" is a child of "connection-definitions=*" (always present) + */ + +public class StatisticsResource extends NamedNode { + + private static final String EXTENDED = "extended"; + private static final String EXTENDED_STATS = STATISTICS + "/" + EXTENDED; + private static final String POOL_STATS = STATISTICS + "/pool"; + + public static final String EXT_STATS_AVAILABLE = "extended-statistics-available"; + + private final String parentName; + private final ResourceType resourceType; + + public StatisticsResource(final Property property) { + this(null, ResourceType.RESOURCE_ADAPTER, property); + } + + public StatisticsResource(String parentName, ResourceType resourceType, final Property property) { + this(parentName, property.getName(), resourceType, property.getValue()); + } + + public StatisticsResource(String parentName, String name, ResourceType resourceType, ModelNode modelNode) { + super(name, modelNode); + this.parentName = parentName; + this.resourceType = resourceType; + } + + public enum ResourceType { + RESOURCE_ADAPTER(RESOURCE_ADAPTER_TEMPLATE), ADMIN_OBJECT(ADMIN_OBJECT_TEMPLATE), CONNECTION_DEFINITION( + CONN_DEF_TEMPLATE); + + private final AddressTemplate template; + + ResourceType(AddressTemplate template) { + this.template = template; + } + + public AddressTemplate getTemplate() { + return template; + } + } + + public String getParentName() { + return parentName; + } + + public ResourceType getResourceType() { + return resourceType; + } + + public boolean hasExtendedStats() { + return has(STATISTICS) && get(STATISTICS).has(EXTENDED); + } + + public ModelNode getExtendedStats() { + return failSafeGet(this, EXTENDED_STATS); + } + + public ModelNode getPoolStats() { + return failSafeGet(this, POOL_STATS); + } + + public boolean isStatisticsEnabled() { + boolean isStatisticsEnabled = getExtendedStats().get(STATISTICS_ENABLED).asBoolean(false); + if (resourceType == ResourceType.CONNECTION_DEFINITION) { + isStatisticsEnabled = isStatisticsEnabled || getPoolStats().get(STATISTICS_ENABLED).asBoolean(false); + } + return isStatisticsEnabled; + } +} diff --git a/core/src/main/java/org/jboss/hal/core/subsystem/Subsystems.java b/core/src/main/java/org/jboss/hal/core/subsystem/Subsystems.java index fcf8b03685..05bc8f0e91 100644 --- a/core/src/main/java/org/jboss/hal/core/subsystem/Subsystems.java +++ b/core/src/main/java/org/jboss/hal/core/subsystem/Subsystems.java @@ -172,7 +172,7 @@ public Subsystems(Resources resources) { .token(NameTokens.REQUEST_CONTROLLER) .preview(resources.previews().configurationRequestController()) .build()); - addConfiguration(new SubsystemMetadata.Builder(RESOURCE_ADAPTERS, "Resource Adapters") + addConfiguration(new SubsystemMetadata.Builder(RESOURCE_ADAPTERS, Names.RESOURCE_ADAPTERS) .nextColumn(Ids.RESOURCE_ADAPTER) .preview(resources.previews().configurationResourceAdapters()) .build()); @@ -258,6 +258,10 @@ public Subsystems(Resources resources) { .subtitle(Names.SMALLRYE) .token(NameTokens.MICRO_PROFILE_HEALTH) .build()); + addRuntime(new SubsystemMetadata.Builder(RESOURCE_ADAPTERS, Names.RESOURCE_ADAPTERS) + .nextColumn(Ids.RESOURCE_ADAPTER_RUNTIME) + .preview(resources.previews().runtimeResourceAdapters()) + .build()); addRuntime(new SubsystemMetadata.Builder(TRANSACTIONS, Names.TRANSACTION) .token(NameTokens.TRANSACTIONS_RUNTIME) .build()); diff --git a/meta/src/main/java/org/jboss/hal/meta/token/NameTokens.java b/meta/src/main/java/org/jboss/hal/meta/token/NameTokens.java index 68322c3a33..c5e10b4826 100644 --- a/meta/src/main/java/org/jboss/hal/meta/token/NameTokens.java +++ b/meta/src/main/java/org/jboss/hal/meta/token/NameTokens.java @@ -103,6 +103,8 @@ public interface NameTokens { String REPLICATED_CACHE = ModelDescriptionConstants.REPLICATED_CACHE; String REQUEST_CONTROLLER = ModelDescriptionConstants.REQUEST_CONTROLLER; String RESOURCE_ADAPTER = ModelDescriptionConstants.RESOURCE_ADAPTER; + String RESOURCE_ADAPTER_CHILD_RUNTIME = ModelDescriptionConstants.RESOURCE_ADAPTER + "-child" + RUNTIME_SUFFIX; + String RESOURCE_ADAPTER_RUNTIME = ModelDescriptionConstants.RESOURCE_ADAPTER + RUNTIME_SUFFIX; String RUNTIME = "runtime"; String SCATTERED_CACHE = ModelDescriptionConstants.SCATTERED_CACHE; String SECURITY_CONFIGURATION = "security"; diff --git a/resources/src/main/java/org/jboss/hal/resources/Ids.java b/resources/src/main/java/org/jboss/hal/resources/Ids.java index 31ded8b1af..a635a2d930 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Ids.java +++ b/resources/src/main/java/org/jboss/hal/resources/Ids.java @@ -667,6 +667,9 @@ public interface Ids { String RESOURCE_ADAPTER_ADMIN_OBJECT_ADD = "resource-adapter-admin-object-add"; String RESOURCE_ADAPTER_CONNECTION_DEFINITION_ADD = "resource-adapter-connection-definition-add"; String RESOURCE_ADAPTER_FORM = "resource-adapter-form"; + String RESOURCE_ADAPTER_CHILD_RUNTIME = "ra-child-runtime"; + String RESOURCE_ADAPTER_CHILD_RUNTIME_TAB_CONTAINER = "ra-runtime-tab-container"; + String RESOURCE_ADAPTER_RUNTIME = "ra-runtime"; String REST_RESOURCE = "rest-rsc"; String REST_RESOURCE_PATH_PARAM_FORM = "rest-rsc-path-param-form"; String REST_RESOURCE_REFRESH = "rest-rsc-refresh"; @@ -997,6 +1000,14 @@ static String resourceAdapter(String name) { return build("ra", name); } + static String resourceAdapterRuntime(String name) { + return build("rar", name); + } + + static String resourceAdapterChildRuntime(String ra, String name) { + return build("rar", ra, name); + } + static String restResource(String deployment, String subdeployment, String name) { return build(deployment, subdeployment, name); } diff --git a/resources/src/main/java/org/jboss/hal/resources/Messages.java b/resources/src/main/java/org/jboss/hal/resources/Messages.java index 9b32ff48cd..8727d6b70e 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Messages.java +++ b/resources/src/main/java/org/jboss/hal/resources/Messages.java @@ -20,6 +20,7 @@ // @formatter:off public interface Messages extends com.google.gwt.i18n.client.Messages { SafeHtml accessMechanismLabel(String name); + SafeHtml activationSuccess(); SafeHtml addError(String type, String identity, String resource, String error); SafeHtml addHaPolicy(); SafeHtml addKeyStoreError(String name); @@ -270,6 +271,7 @@ public interface Messages extends com.google.gwt.i18n.client.Messages { SafeHtml noReferenceServerPreview(String deployment, String attribute1, String attribute2, String serverGroup, String historyToken); SafeHtml noReset(); SafeHtml noResource(); + SafeHtml noStatistics(); SafeHtml normalLogFile(String size); SafeHtml noSecuritySettingSelected(); SafeHtml noSelectedPatch(); diff --git a/resources/src/main/java/org/jboss/hal/resources/Names.java b/resources/src/main/java/org/jboss/hal/resources/Names.java index bacfa77bbf..42f32ed321 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Names.java +++ b/resources/src/main/java/org/jboss/hal/resources/Names.java @@ -309,6 +309,7 @@ public interface Names { String RESOLVERS = "Resolvers"; String RESOURCE = "Resource"; String RESOURCE_ADAPTER = "Resource Adapter"; + String RESOURCE_ADAPTERS = "Resource Adapters"; String RESOURCE_PATHS = "Resource Paths"; String RESPONSE_HEADER = "Response Header"; String REST_RESOURCE = "REST Resource"; diff --git a/resources/src/main/java/org/jboss/hal/resources/Previews.java b/resources/src/main/java/org/jboss/hal/resources/Previews.java index f6840444b5..1af1e9c907 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Previews.java +++ b/resources/src/main/java/org/jboss/hal/resources/Previews.java @@ -339,6 +339,9 @@ public interface Previews extends ClientBundleWithLookup { @Source("previews/runtime/mod-cluster.html") ExternalTextResource runtimeModCluster(); + @Source("previews/runtime/resource-adapters.html") + ExternalTextResource runtimeResourceAdapters(); + @Source("previews/runtime/security-elytron.html") ExternalTextResource runtimeSecurityElytron(); diff --git a/resources/src/main/resources/org/jboss/hal/resources/Messages.properties b/resources/src/main/resources/org/jboss/hal/resources/Messages.properties index 037fd34eb5..c7f2507964 100644 --- a/resources/src/main/resources/org/jboss/hal/resources/Messages.properties +++ b/resources/src/main/resources/org/jboss/hal/resources/Messages.properties @@ -17,6 +17,7 @@ # Please encode single apostrophs as ' accessControlSsoDescription=Keycloak Single Sign-On for the HTTP management interface is enabled for this server. The user and roles are managed in keycloak server, see below the configuration details. accessMechanismLabel=

Access Mechanism: {0}

+activationSuccess=Activation successful activeRoles=Active roles: {0} addError=There was and error trying to add an {0} {1} to {2}. Cause: {3} addHaPolicy=No HA policy has been assigned to this server. Please use the button below to add a HA policy. You can choose between replication and shared store. For each strategy you can select whether to act as a primary, secondary or operate in colocated mode. @@ -354,6 +355,7 @@ noReferenceServerEmptyState=No runtime information available for deployment {0}. No running server was found, which contains this deployment. Go to server group {3} and start a server in order to see all details. noReset=None of the attributes could be reset. noResource=No resource was found for the selected item. +noStatistics=No statistics available for this resource. normalLogFile=The log file has a size of {0}. noSecuritySettingSelected=No security setting selected. noSelectedPatch=No patch file specified. Please select a file patch to upload. diff --git a/resources/src/main/resources/org/jboss/hal/resources/previews/runtime/resource-adapters.html b/resources/src/main/resources/org/jboss/hal/resources/previews/runtime/resource-adapters.html new file mode 100644 index 0000000000..3789f13d63 --- /dev/null +++ b/resources/src/main/resources/org/jboss/hal/resources/previews/runtime/resource-adapters.html @@ -0,0 +1 @@ +

Displays runtime statistics for resource adapters, administration objects and connection definitions.