/* * * JBoss, Home of Professional Open Source. * Copyright 2013, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * / */ package org.wildfly.extension.io; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationStepHandler; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.PersistentResourceDefinition; import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; import org.jboss.as.controller.capability.RuntimeCapability; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.registry.AttributeAccess; import org.jboss.as.controller.registry.DelegatingResource; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.as.controller.registry.PlaceholderResource; import org.jboss.as.controller.registry.Resource; import org.jboss.as.controller.registry.ResourceProvider; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceRegistry; import org.wildfly.extension.io.logging.IOLogger; import org.xnio.Option; import org.xnio.Options; import org.xnio.XnioWorker; import org.xnio.management.XnioWorkerMXBean; /** * @author <a href="mailto:tomaz.cerar@redhat.com">Tomaz Cerar</a> (c) 2012 Red Hat Inc. */ class WorkerResourceDefinition extends PersistentResourceDefinition { static final String IO_WORKER_RUNTIME_CAPABILITY_NAME = "org.wildfly.io.worker"; static final RuntimeCapability<Void> IO_WORKER_RUNTIME_CAPABILITY = RuntimeCapability.Builder.of(IO_WORKER_RUNTIME_CAPABILITY_NAME, true, XnioWorker.class) .build(); static final OptionAttributeDefinition WORKER_TASK_MAX_THREADS = new OptionAttributeDefinition.Builder(Constants.WORKER_TASK_MAX_THREADS, Options.WORKER_TASK_MAX_THREADS) .setFlags(AttributeAccess.Flag.RESTART_ALL_SERVICES) .build(); static final OptionAttributeDefinition WORKER_TASK_KEEPALIVE = new OptionAttributeDefinition.Builder(Constants.WORKER_TASK_KEEPALIVE, Options.WORKER_TASK_KEEPALIVE) .setFlags(AttributeAccess.Flag.RESTART_ALL_SERVICES) .setDefaultValue(new ModelNode(60)) .build(); static final OptionAttributeDefinition STACK_SIZE = new OptionAttributeDefinition.Builder(Constants.STACK_SIZE, Options.STACK_SIZE) .setFlags(AttributeAccess.Flag.RESTART_ALL_SERVICES) .setDefaultValue(new ModelNode(0L)) .build(); static final OptionAttributeDefinition WORKER_IO_THREADS = new OptionAttributeDefinition.Builder(Constants.WORKER_IO_THREADS, Options.WORKER_IO_THREADS) .setFlags(AttributeAccess.Flag.RESTART_ALL_SERVICES) .build(); static OptionAttributeDefinition[] ATTRIBUTES = new OptionAttributeDefinition[]{ WORKER_IO_THREADS, WORKER_TASK_KEEPALIVE, WORKER_TASK_MAX_THREADS, STACK_SIZE }; private static final AttributeDefinition SHUTDOWN_REQUESTED = new SimpleAttributeDefinitionBuilder("shutdown-requested", ModelType.BOOLEAN).setStorageRuntime().build(); private static final AttributeDefinition CORE_WORKER_POOL_SIZE = new SimpleAttributeDefinitionBuilder("core-pool-size", ModelType.INT).setStorageRuntime().build(); private static final AttributeDefinition MAX_WORKER_POOL_SIZE = new SimpleAttributeDefinitionBuilder("max-pool-size", ModelType.INT).setStorageRuntime().build(); private static final AttributeDefinition IO_THREAD_COUNT = new SimpleAttributeDefinitionBuilder("io-thread-count", ModelType.INT).setStorageRuntime().build(); private static final AttributeDefinition QUEUE_SIZE = new SimpleAttributeDefinitionBuilder("queue-size", ModelType.INT).setStorageRuntime().build(); static final Map<String, OptionAttributeDefinition> ATTRIBUTES_BY_XMLNAME; static { Map<String, OptionAttributeDefinition> attrs = new HashMap<>(); for (AttributeDefinition attr : ATTRIBUTES) { attrs.put(attr.getXmlName(), (OptionAttributeDefinition) attr); } ATTRIBUTES_BY_XMLNAME = Collections.unmodifiableMap(attrs); } static final WorkerResourceDefinition INSTANCE = new WorkerResourceDefinition(); private WorkerResourceDefinition() { super(IOExtension.WORKER_PATH, IOExtension.getResolver(Constants.WORKER), WorkerAdd.INSTANCE, new ReloadRequiredRemoveStepHandler() ); } @SuppressWarnings("unchecked") @Override public Collection<AttributeDefinition> getAttributes() { return (Collection) ATTRIBUTES_BY_XMLNAME.values(); } @Override public void registerCapabilities(ManagementResourceRegistration resourceRegistration) { resourceRegistration.registerCapability(IO_WORKER_RUNTIME_CAPABILITY); } @Override public void registerAttributes(ManagementResourceRegistration resourceRegistration) { for (OptionAttributeDefinition attr: ATTRIBUTES){ resourceRegistration.registerReadOnlyAttribute(attr, new WorkerReadAttributeHandler(attr.getOption())); } WorkerMetricsHandler metricsHandler = new WorkerMetricsHandler(); resourceRegistration.registerMetric(SHUTDOWN_REQUESTED, metricsHandler); resourceRegistration.registerMetric(CORE_WORKER_POOL_SIZE, metricsHandler); resourceRegistration.registerMetric(MAX_WORKER_POOL_SIZE, metricsHandler); resourceRegistration.registerMetric(IO_THREAD_COUNT, metricsHandler); resourceRegistration.registerMetric(QUEUE_SIZE, metricsHandler); } @Override public void registerChildren(ManagementResourceRegistration resourceRegistration) { super.registerChildren(resourceRegistration); resourceRegistration.registerSubModel(new WorkerServerDefinition()); } private abstract static class AbstractWorkerAttributeHandler implements OperationStepHandler { public void execute(OperationContext outContext, ModelNode operation) throws OperationFailedException { outContext.addStep((context, op) -> { XnioWorker worker = getXnioWorker(context); if (worker == null || worker.getMXBean() == null) { context.getResult().set(IOExtension.NO_METRICS); return; } executeWithWorker(context, op, worker); }, OperationContext.Stage.RUNTIME); } abstract void executeWithWorker(OperationContext context, ModelNode operation, XnioWorker worker) throws OperationFailedException; } private static class WorkerReadAttributeHandler extends AbstractWorkerAttributeHandler { final Option<?> option; public WorkerReadAttributeHandler(Option<?> option) { this.option = option; } @Override void executeWithWorker(OperationContext context, ModelNode operation, XnioWorker worker) throws OperationFailedException { try { Object result = worker.getOption(option); if (result!=null){ context.getResult().set(new ModelNode(result.toString())); } } catch (IOException e) { throw new OperationFailedException(e); } } } private static class WorkerMetricsHandler extends AbstractWorkerAttributeHandler{ @Override void executeWithWorker(OperationContext context, ModelNode operation, XnioWorker worker) throws OperationFailedException { XnioWorkerMXBean metrics = worker.getMXBean(); String name = operation.require(ModelDescriptionConstants.NAME).asString(); context.getResult().set(getMetricValue(name, metrics)); } } static XnioWorker getXnioWorker(OperationContext context) { String name = context.getCurrentAddressValue(); if (!context.getCurrentAddress().getLastElement().getKey().equals(IOExtension.WORKER_PATH.getKey())) { //we are somewhere deeper, lets find worker name for (PathElement pe : context.getCurrentAddress()) { if (pe.getKey().equals(IOExtension.WORKER_PATH.getKey())) { name = pe.getValue(); break; } } } return getXnioWorker(context.getServiceRegistry(false), name); } static XnioWorker getXnioWorker(ServiceRegistry serviceRegistry, String name) { ServiceName serviceName = IO_WORKER_RUNTIME_CAPABILITY.getCapabilityServiceName(name, XnioWorker.class); ServiceController<XnioWorker> controller = (ServiceController<XnioWorker>) serviceRegistry.getService(serviceName); if (controller == null || controller.getState() != ServiceController.State.UP) { return null; } return controller.getValue(); } private static XnioWorkerMXBean getMetrics(ServiceRegistry serviceRegistry, String name) { XnioWorker worker = getXnioWorker(serviceRegistry, name); if (worker != null && worker.getMXBean() != null) { return worker.getMXBean(); } return null; } private static ModelNode getMetricValue(String attributeName, XnioWorkerMXBean metric) throws OperationFailedException { if (SHUTDOWN_REQUESTED.getName().equals(attributeName)) { return new ModelNode(metric.isShutdownRequested()); } else if (CORE_WORKER_POOL_SIZE.getName().equals(attributeName)) { return new ModelNode(metric.getCoreWorkerPoolSize()); } else if (MAX_WORKER_POOL_SIZE.getName().equals(attributeName)) { return new ModelNode(metric.getMaxWorkerPoolSize()); } else if (IO_THREAD_COUNT.getName().equals(attributeName)) { return new ModelNode(metric.getIoThreadCount()); } else if (QUEUE_SIZE.getName().equals(attributeName)) { return new ModelNode(metric.getWorkerQueueSize()); } else { throw new OperationFailedException(IOLogger.ROOT_LOGGER.noMetrics()); } } static class WorkerResource extends DelegatingResource { private final ServiceRegistry serviceRegistry; private final PathAddress pathAddress; public WorkerResource(OperationContext context) { super(Resource.Factory.create()); this.serviceRegistry = context.getServiceRegistry(false); this.pathAddress = context.getCurrentAddress(); super.registerResourceProvider("server", new ResourceProvider() { @Override public boolean has(String name) { return children().contains(name); } @Override public Resource get(String name) { return PlaceholderResource.INSTANCE; } @Override public boolean hasChildren() { return false; } @Override public Set<String> children() { XnioWorkerMXBean metrics = getMetrics(serviceRegistry, pathAddress.getLastElement().getValue()); if (metrics == null) { return Collections.emptySet(); } Set<String> res = new LinkedHashSet<>(); metrics.getServerMXBeans().forEach(serverMXBean -> res.add(serverMXBean.getBindAddress())); return res; } @Override public void register(String name, Resource resource) { throw new UnsupportedOperationException(); } @Override public void register(String value, int index, Resource resource) { throw new UnsupportedOperationException(); } @Override public Resource remove(String name) { return null; } @Override public ResourceProvider clone() { return this; } }); } @Override public Set<String> getChildTypes() { return Collections.singleton("server"); } } }