/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2019, 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.microprofile.metrics;

import static org.jboss.as.controller.PathAddress.EMPTY_ADDRESS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ATTRIBUTES;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBDEPLOYMENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.wildfly.extension.microprofile.metrics.UnitConverter.unitSuffix;
import static org.wildfly.extension.microprofile.metrics._private.MicroProfileMetricsLogger.LOGGER;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import io.prometheus.client.Collector.MetricFamilySamples;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.CounterMetricFamily;
import io.prometheus.client.GaugeMetricFamily;
import org.jboss.as.controller.LocalModelControllerClient;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.client.helpers.MeasurementUnit;
import org.jboss.as.controller.descriptions.DescriptionProvider;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.registry.AttributeAccess;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;

public class MetricCollector {

    private final PrometheusCollector prometheusCollector;
    private final boolean exposeAnySubsystem;
    private String globalPrefix;
    private final List<String> exposedSubsystems;
    private final LocalModelControllerClient modelControllerClient;

    public MetricCollector(LocalModelControllerClient modelControllerClient, List<String> exposedSubsystems, String globalPrefix) {
        this.modelControllerClient = modelControllerClient;
        this.exposedSubsystems = exposedSubsystems;
        this.exposeAnySubsystem = exposedSubsystems.remove("*");
        this.globalPrefix = globalPrefix;
        this.prometheusCollector = new PrometheusCollector();
        this.prometheusCollector.register(CollectorRegistry.defaultRegistry);
    }

    void close() {
        CollectorRegistry.defaultRegistry.unregister(prometheusCollector);
    }

    // collect metrics from the resources
    public MetricRegistration collectResourceMetrics(final Resource resource,
                                              ImmutableManagementResourceRegistration managementResourceRegistration,
                                              Function<PathAddress, PathAddress> resourceAddressResolver) {
        MetricRegistration registration = new MetricRegistration();
        collectResourceMetrics0(resource, managementResourceRegistration, EMPTY_ADDRESS, resourceAddressResolver, registration);
        return registration;
    }

    private void collectResourceMetrics0(final Resource current,
                                        ImmutableManagementResourceRegistration managementResourceRegistration,
                                         PathAddress address,
                                         Function<PathAddress, PathAddress> resourceAddressResolver,
                                        MetricRegistration registration) {
        if (!isExposingMetrics(address)) {
            return;
        }

        Map<String, AttributeAccess> attributes = managementResourceRegistration.getAttributes(address);
        if (attributes == null) {
            return;
        }

        ModelNode resourceDescription = null;
        for (Map.Entry<String, AttributeAccess> entry : attributes.entrySet()) {
            String attributeName = entry.getKey();

            AttributeAccess attributeAccess = entry.getValue();
            if (!isCollectibleMetric(attributeAccess)) {
                continue;
            }

            if (resourceDescription == null) {
                DescriptionProvider modelDescription = managementResourceRegistration.getModelDescription(address);
                resourceDescription = modelDescription.getModelDescription(Locale.getDefault());
            }
            PathAddress resourceAddress = resourceAddressResolver.apply(address);
            MeasurementUnit unit = attributeAccess.getAttributeDefinition().getMeasurementUnit();
            boolean counter = attributeAccess.getFlags().contains(AttributeAccess.Flag.COUNTER_METRIC);
            MetricMetadata metricMetadata = new MetricMetadata(attributeName, resourceAddress, unit, globalPrefix, counter);
            String attributeDescription = resourceDescription.get(ATTRIBUTES, attributeName, DESCRIPTION).asStringOrNull();
            final MetricFamilySamples metricFamilySamples;
            if (counter) {
                metricFamilySamples = new CounterMetricFamily(metricMetadata.metricName, attributeDescription, metricMetadata.labelNames);
            } else {
                metricFamilySamples = new GaugeMetricFamily(metricMetadata.metricName, attributeDescription, metricMetadata.labelNames);
            }
            Supplier<Optional<MetricFamilySamples.Sample>> sampleSupplier = () -> {
                final ModelNode readAttributeOp = new ModelNode();
                readAttributeOp.get(OP).set(READ_ATTRIBUTE_OPERATION);
                readAttributeOp.get(OP_ADDR).set(resourceAddress.toModelNode());
                readAttributeOp.get(ModelDescriptionConstants.INCLUDE_UNDEFINED_METRIC_VALUES).set(true);
                readAttributeOp.get(NAME).set(attributeName);
                ModelNode response = modelControllerClient.execute(readAttributeOp);
                String error = getFailureDescription(response);
                if (error != null) {
                    throw LOGGER.unableToReadAttribute(attributeName, address, error);
                }
                ModelNode result = response.get(RESULT);
                if (result.isDefined()) {
                    try {
                        double initialValue = result.asDouble();
                        double scaledValue = UnitConverter.scaleToBase(initialValue, unit);
                        return Optional.of(new MetricFamilySamples.Sample(metricMetadata.metricName, metricMetadata.labelNames, metricMetadata.labelValues, scaledValue));
                    } catch (Exception e) {
                        throw LOGGER.unableToConvertAttribute(attributeName, address, e);
                    }
                }
                return Optional.empty();
            };
            prometheusCollector.addMetricFamilySampleSupplier(metricFamilySamples, sampleSupplier);
            registration.addUnregistrationTask(() -> prometheusCollector.removeMetricFamilySampleSupplier(metricMetadata.metricName, sampleSupplier));
        }

        for (String type : current.getChildTypes()) {
            if (current.hasChildren(type)) {
                for (Resource.ResourceEntry entry : current.getChildren(type)) {
                    final PathElement pathElement = entry.getPathElement();
                    final PathAddress childAddress = address.append(pathElement);
                    collectResourceMetrics0(entry, managementResourceRegistration, childAddress, resourceAddressResolver, registration);
                }
            }
        }
    }

    private boolean isExposingMetrics(PathAddress address) {
        // root resource
        if (address.size() == 0) {
            return true;
        }
        String subsystemName = getSubsystemName(address);
        if (subsystemName != null) {
            return exposeAnySubsystem || exposedSubsystems.contains(subsystemName);
        }
        // do not expose metrics for resources outside the subsystems and deployments.
        return false;
    }

    private String getSubsystemName(PathAddress address) {
        if (address.size() == 0) {
            return null;
        }
        if (address.getElement(0).getKey().equals(SUBSYSTEM)) {
            return address.getElement(0).getValue();
        } else {
            return getSubsystemName(address.subAddress(1));
        }
    }

    void collectMetricFamilies(ImmutableManagementResourceRegistration managementResourceRegistration,
                                       final PathAddress address) {
        if (!isExposingMetrics(address)) {
            return;
        }

        ModelNode resourceDescription = null;
        Map<String, AttributeAccess> attributes = managementResourceRegistration.getAttributes(address);
        for (Map.Entry<String, AttributeAccess> entry : attributes.entrySet()) {
            String attributeName = entry.getKey();
            AttributeAccess attributeAccess = entry.getValue();
            if (!isCollectibleMetric(attributeAccess)) {
                LOGGER.debugf("Type %s is not supported for MicroProfile Metrics, the attribute %s on %s will not be registered.",
                        attributeAccess.getAttributeDefinition().getType(), attributeName, address);
                continue;
            }
            if (resourceDescription == null) {
                DescriptionProvider modelDescription = managementResourceRegistration.getModelDescription(address);
                resourceDescription = modelDescription.getModelDescription(Locale.getDefault());
            }
            boolean counter = attributeAccess.getFlags().contains(AttributeAccess.Flag.COUNTER_METRIC);
            MetricMetadata metricMetadata = new MetricMetadata(attributeName, address, attributeAccess.getAttributeDefinition().getMeasurementUnit(), globalPrefix, counter);
            String attributeDescription = resourceDescription.get(ATTRIBUTES, attributeName, DESCRIPTION).asStringOrNull();
            final MetricFamilySamples metricFamilySamples;
            if (counter) {
                metricFamilySamples = new CounterMetricFamily(metricMetadata.metricName, attributeDescription, metricMetadata.labelNames);
            } else {
                metricFamilySamples = new GaugeMetricFamily(metricMetadata.metricName, attributeDescription, metricMetadata.labelNames);
            }
            prometheusCollector.addMetricFamilySamples(metricFamilySamples);
        }

        for (PathElement childAddress : managementResourceRegistration.getChildAddresses(address)) {
            collectMetricFamilies(managementResourceRegistration, address.append(childAddress));
        }
    }

    private boolean isCollectibleMetric(AttributeAccess attributeAccess) {
        if (attributeAccess.getAccessType() == AttributeAccess.AccessType.METRIC
                && attributeAccess.getStorageType() == AttributeAccess.Storage.RUNTIME) {
            // handle only metrics with simple numerical types
            ModelType type = attributeAccess.getAttributeDefinition().getType();
            if (type == ModelType.INT ||
                    type == ModelType.LONG ||
                    type == ModelType.DOUBLE) {
                return true;
            }
        }
        return false;
    }

    private String getFailureDescription(ModelNode result) {
        if (result.hasDefined(FAILURE_DESCRIPTION)) {
            return result.get(FAILURE_DESCRIPTION).toString();
        }
        return null;
    }

    public static final class MetricRegistration {

        private final List<Runnable> unregistrationTasks = new ArrayList<>();

        MetricRegistration() {
        }

        public void unregister() {
            for (Runnable task : unregistrationTasks) {
                task.run();
            }
        }

        private void addUnregistrationTask(Runnable task) {
            unregistrationTasks.add(task);
        }
    }

    private static class MetricMetadata {

        private static final Pattern SNAKE_CASE_PATTERN = Pattern.compile("(?<=[a-z])[A-Z]");

        private final String metricName;
        private final List<String> labelNames;
        private final List<String> labelValues;

        MetricMetadata(String attributeName, PathAddress address, MeasurementUnit unit, String globalPrefix, boolean counter) {
            String metricPrefix = "";
            labelNames = new ArrayList<>();
            labelValues = new ArrayList<>();
            for (PathElement element: address) {
                String key = element.getKey();
                String value = element.getValue();
                // prepend the subsystem or statistics name to the attribute
                if (key.equals(SUBSYSTEM) || key.equals("statistics")) {
                    metricPrefix += value + "_";
                    continue;
                }
                labelNames.add(getPrometheusMetricName(key, null, false));
                labelValues.add(value);
            }
            // if the resource address defines a deployment (without subdeployment),
            if (labelNames.contains(DEPLOYMENT)  && !labelNames.contains(SUBDEPLOYMENT)) {
                labelNames.add(SUBDEPLOYMENT);
                labelValues.add(labelValues.get(labelNames.indexOf(DEPLOYMENT)));
            }
            if (globalPrefix != null && !globalPrefix.isEmpty()) {
                metricPrefix = globalPrefix + "_" + metricPrefix;
            }
            metricName = getPrometheusMetricName(metricPrefix + attributeName, unit, counter);
        }

        private static String getPrometheusMetricName(String name, MeasurementUnit unit, boolean counter) {
            String prometheusName = name + unitSuffix(unit) + (counter ? "_total" : "");
            prometheusName =prometheusName.replaceAll("[^\\w]+","_");
            prometheusName = decamelize(prometheusName);
            return prometheusName;
        }

        private static String decamelize(String in) {
            Matcher m = SNAKE_CASE_PATTERN.matcher(in);
            StringBuffer sb = new StringBuffer();
            while (m.find()) {
                m.appendReplacement(sb, "_" + m.group().toLowerCase());
            }
            m.appendTail(sb);
            return sb.toString().toLowerCase();
        }
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014, 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.jboss.as.webservices.publish;

import java.io.File;
import java.security.AccessController;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.jboss.as.server.CurrentServiceContainer;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.web.host.ServletBuilder;
import org.jboss.as.web.host.WebDeploymentBuilder;
import org.jboss.as.web.host.WebDeploymentController;
import org.jboss.as.web.host.WebHost;
import org.jboss.as.webservices.config.ServerConfigFactoryImpl;
import org.jboss.as.webservices.logging.WSLogger;
import org.jboss.as.webservices.deployers.AllowWSRequestPredicate;
import org.jboss.as.webservices.deployers.EndpointServiceDeploymentAspect;
import org.jboss.as.webservices.deployers.deployment.DeploymentAspectsProvider;
import org.jboss.as.webservices.deployers.deployment.WSDeploymentBuilder;
import org.jboss.as.webservices.service.EndpointService;
import org.jboss.as.webservices.util.WSAttachmentKeys;
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.web.jboss.JBossServletMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.metadata.web.spec.ServletMappingMetaData;
import org.jboss.msc.service.ServiceContainer;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceRegistry;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.service.StabilityMonitor;
import org.jboss.ws.common.deployment.DeploymentAspectManagerImpl;
import org.jboss.ws.common.deployment.EndpointHandlerDeploymentAspect;
import org.jboss.ws.common.integration.AbstractDeploymentAspect;
import org.jboss.ws.common.invocation.InvocationHandlerJAXWS;
import org.jboss.wsf.spi.classloading.ClassLoaderProvider;
import org.jboss.wsf.spi.deployment.Deployment;
import org.jboss.wsf.spi.deployment.DeploymentAspect;
import org.jboss.wsf.spi.deployment.DeploymentAspectManager;
import org.jboss.wsf.spi.deployment.Endpoint;
import org.jboss.wsf.spi.deployment.WSFServlet;
import org.jboss.wsf.spi.metadata.webservices.JBossWebservicesMetaData;
import org.jboss.wsf.spi.metadata.webservices.WebservicesMetaData;
import org.jboss.wsf.spi.publish.Context;
import org.jboss.wsf.spi.publish.EndpointPublisher;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * WS endpoint publisher, allows for publishing a WS endpoint on AS 7
 *
 * @author alessio.soldano@jboss.com
 * @since 12-Jul-2011
 */
public final class EndpointPublisherImpl implements EndpointPublisher {

    private final WebHost host;
    private final boolean runningInService;
    private static List<DeploymentAspect> publisherDepAspects = null;
    private static List<DeploymentAspect> depAspects = null;

    protected EndpointPublisherImpl(WebHost host) {
        this(host, false);
    }

    protected EndpointPublisherImpl(WebHost host, boolean runningInService) {
        this.host = host;
        this.runningInService = runningInService;
    }

    protected EndpointPublisherImpl(boolean runningInService) {
        this(null, runningInService);
    }

    @Override
    public Context publish(String context, ClassLoader loader, Map<String, String> urlPatternToClassNameMap) throws Exception {
        return publish(currentServiceContainer(), context, loader, urlPatternToClassNameMap, null, null, null);
    }

    @Override
    public Context publish(String context, ClassLoader loader, Map<String, String> urlPatternToClassNameMap, WebservicesMetaData metadata) throws Exception {
        return publish(currentServiceContainer(), context, loader, urlPatternToClassNameMap, null, metadata, null);
    }

    @Override
    public Context publish(String context, ClassLoader loader, Map<String, String> urlPatternToClassNameMap,
            WebservicesMetaData metadata, JBossWebservicesMetaData jbwsMetadata) throws Exception {
        return publish(currentServiceContainer(), context, loader, urlPatternToClassNameMap, null, metadata, jbwsMetadata);
    }

    protected Context publish(ServiceTarget target, String context, ClassLoader loader,
            Map<String, String> urlPatternToClassNameMap, JBossWebMetaData jbwmd, WebservicesMetaData metadata, JBossWebservicesMetaData jbwsMetadata)
            throws Exception {
        DeploymentUnit unit = doPrepare(context, loader, urlPatternToClassNameMap, jbwmd, metadata, jbwsMetadata);
        doDeploy(target, unit);
        return doPublish(target, unit);
    }

    /**
     * Prepare the ws Deployment and return a DeploymentUnit containing it
     *
     * @param context
     * @param loader
     * @param urlPatternToClassNameMap
     * @param jbwmd
     * @param metadata
     * @param jbwsMetadata
     * @return
     */
    protected DeploymentUnit doPrepare(String context, ClassLoader loader,
            Map<String, String> urlPatternToClassNameMap, JBossWebMetaData jbwmd, WebservicesMetaData metadata, JBossWebservicesMetaData jbwsMetadata) {
        ClassLoader origClassLoader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
        WSEndpointDeploymentUnit unit = new WSEndpointDeploymentUnit(loader, context, urlPatternToClassNameMap, jbwmd, metadata, jbwsMetadata);
        try {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(ClassLoaderProvider.getDefaultProvider().getServerIntegrationClassLoader());
            WSDeploymentBuilder.getInstance().build(unit);
            return unit;
        } finally {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(origClassLoader);
        }
    }

    /**
     * Triggers the WS deployment aspects, which process the deployment and
     * install the endpoint services.
     *
     * @param target
     * @param unit
     */
    protected void doDeploy(ServiceTarget target, DeploymentUnit unit) {
        List<DeploymentAspect> aspects = getDeploymentAspects();
        ClassLoader origClassLoader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
        Deployment dep = null;
        try {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(ClassLoaderProvider.getDefaultProvider().getServerIntegrationClassLoader());
            dep = unit.getAttachment(WSAttachmentKeys.DEPLOYMENT_KEY);
            dep.addAttachment(ServiceTarget.class, target);
            DeploymentAspectManager dam = new DeploymentAspectManagerImpl();
            dam.setDeploymentAspects(aspects);
            dam.deploy(dep);
        } finally {
            if (dep != null) {
                dep.removeAttachment(ServiceTarget.class);
            }
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(origClassLoader);
        }
    }

    /**
     * Publish the webapp for the WS deployment unit
     *
     * @param target
     * @param unit
     * @return
     * @throws Exception
     */
    protected Context doPublish(ServiceTarget target, DeploymentUnit unit) throws Exception {
        Deployment deployment = unit.getAttachment(WSAttachmentKeys.DEPLOYMENT_KEY);
        List<Endpoint> endpoints = deployment.getService().getEndpoints();
        //If we're running in a Service, that will already have proper dependencies set on the installed endpoint services,
        //otherwise we need to explicitly wait for the endpoint services to be started before creating the webapp.
        if (!runningInService) {
            final ServiceRegistry registry = unit.getServiceRegistry();
            final StabilityMonitor monitor = new StabilityMonitor();
            for (Endpoint ep : endpoints) {
                final ServiceName serviceName = EndpointService.getServiceName(unit, ep.getShortName());
                monitor.addController(registry.getRequiredService(serviceName));
            }
            try {
                monitor.awaitStability();
            } finally {
                monitor.clear();
            }
        }
        deployment.addAttachment(WebDeploymentController.class, startWebApp(host, unit)); //TODO simplify and use findChild later in destroy()/stopWebApp()
        return new Context(unit.getAttachment(WSAttachmentKeys.JBOSSWEB_METADATA_KEY).getContextRoot(), endpoints);
    }

    private static WebDeploymentController startWebApp(WebHost host, DeploymentUnit unit) throws Exception {
        WebDeploymentBuilder deployment = new WebDeploymentBuilder();
        WebDeploymentController handle;
        try {
            JBossWebMetaData jbwebMD = unit.getAttachment(WSAttachmentKeys.JBOSSWEB_METADATA_KEY);
            deployment.setContextRoot(jbwebMD.getContextRoot());
            File docBase = new File(ServerConfigFactoryImpl.getConfig().getServerTempDir(), jbwebMD.getContextRoot());
            if (!docBase.exists()) {
                docBase.mkdirs();
            }
            deployment.setDocumentRoot(docBase);
            deployment.setClassLoader(unit.getAttachment(WSAttachmentKeys.CLASSLOADER_KEY));
            deployment.addAllowedRequestPredicate(new AllowWSRequestPredicate());

            addServlets(jbwebMD, deployment);

            handle = host.addWebDeployment(deployment);
            handle.create();
        } catch (Exception e) {
            throw WSLogger.ROOT_LOGGER.createContextPhaseFailed(e);
        }
        try {
            handle.start();
        } catch (Exception e) {
            throw WSLogger.ROOT_LOGGER.startContextPhaseFailed(e);
        }
        return handle;
    }

    private static void addServlets(JBossWebMetaData jbwebMD, WebDeploymentBuilder deployment) {
        for (JBossServletMetaData smd : jbwebMD.getServlets()) {
            final String sc = smd.getServletClass();
            if (sc.equals(WSFServlet.class.getName())) {
                ServletBuilder servletBuilder = new ServletBuilder();
                final String servletName = smd.getServletName();

                List<ParamValueMetaData> params = smd.getInitParam();
                List<String> urlPatterns = null;
                for (ServletMappingMetaData smmd : jbwebMD.getServletMappings()) {
                    if (smmd.getServletName().equals(servletName)) {
                        urlPatterns = smmd.getUrlPatterns();
                        servletBuilder.addUrlMappings(urlPatterns);
                        break;
                    }
                }

                WSFServlet wsfs = new WSFServlet();
                servletBuilder.setServletName(servletName);
                servletBuilder.setServlet(wsfs);
                servletBuilder.setServletClass(WSFServlet.class);
                for (ParamValueMetaData param : params) {
                    servletBuilder.addInitParam(param.getParamName(), param.getParamValue());
                }
                deployment.addServlet(servletBuilder);
            }
        }
    }

    @Override
    public void destroy(Context context) throws Exception {
        List<Endpoint> eps = context.getEndpoints();
        if (eps == null || eps.isEmpty()) {
            return;
        }
        Deployment dep = eps.get(0).getService().getDeployment();
        try {
            stopWebApp(dep);
        } finally {
            undeploy(dep);
        }
    }

    /**
     * Triggers the WS deployment aspects, which process
     * the deployment unit and stop the endpoint services.
     *
     * @throws Exception
     */
    protected void undeploy(DeploymentUnit unit) throws Exception {
        undeploy(unit.getAttachment(WSAttachmentKeys.DEPLOYMENT_KEY));
    }

    protected void undeploy(Deployment deployment) throws Exception {
        List<DeploymentAspect> aspects = getDeploymentAspects();
        ClassLoader origClassLoader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
        try {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(ClassLoaderProvider.getDefaultProvider().getServerIntegrationClassLoader());
            DeploymentAspectManager dam = new DeploymentAspectManagerImpl();
            dam.setDeploymentAspects(aspects);
            dam.undeploy(deployment);
        } finally {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(origClassLoader);
        }
    }

    /**
     * Stops the webapp serving the provided ws deployment
     *
     * @param deployment
     * @throws Exception
     */
    protected void stopWebApp(Deployment deployment) throws Exception {
        WebDeploymentController context;
        try {
            context = deployment.getAttachment(WebDeploymentController.class);
            context.stop();
        } catch (Exception e) {
            throw WSLogger.ROOT_LOGGER.stopContextPhaseFailed(e);
        }
        try {
            context.destroy();
        } catch (Exception e) {
            throw WSLogger.ROOT_LOGGER.destroyContextPhaseFailed(e);
        }
    }

    private List<DeploymentAspect> getDeploymentAspects() {
        return runningInService ? getReplacedDeploymentAspects() : getPublisherDeploymentAspects();
    }

    private static synchronized List<DeploymentAspect> getReplacedDeploymentAspects() {
        if (depAspects == null) {
            depAspects = new LinkedList<DeploymentAspect>();
            List<DeploymentAspect> serverAspects = DeploymentAspectsProvider.getSortedDeploymentAspects();
            for (DeploymentAspect aspect : serverAspects) {
                if(aspect instanceof EndpointHandlerDeploymentAspect) {
                    depAspects.add(aspect);
                    //add another aspect to set InvocationHandlerJAXWS to each endpoint
                    ForceJAXWSInvocationHandlerDeploymentAspect handlerAspect = new ForceJAXWSInvocationHandlerDeploymentAspect();
                    depAspects.add(handlerAspect);
                } else {
                    depAspects.add(aspect);
                }
            }
        }
        return depAspects;
    }

    private static synchronized List<DeploymentAspect> getPublisherDeploymentAspects() {
        if (publisherDepAspects == null) {
            publisherDepAspects = new LinkedList<DeploymentAspect>();
            // copy to replace the EndpointServiceDeploymentAspect
            List<DeploymentAspect> serverAspects = DeploymentAspectsProvider.getSortedDeploymentAspects();
            for (DeploymentAspect aspect : serverAspects) {
                if (aspect instanceof EndpointServiceDeploymentAspect) {
                    final EndpointServiceDeploymentAspect a = (EndpointServiceDeploymentAspect) aspect;
                    EndpointServiceDeploymentAspect clone = (EndpointServiceDeploymentAspect) (a.clone());
                    clone.setStopServices(true);
                    publisherDepAspects.add(clone);
                } else if(aspect instanceof EndpointHandlerDeploymentAspect) {
                    publisherDepAspects.add(aspect);
                    //add another aspect to set InvocationHandlerJAXWS to each endpoint
                    ForceJAXWSInvocationHandlerDeploymentAspect handlerAspect = new ForceJAXWSInvocationHandlerDeploymentAspect();
                    publisherDepAspects.add(handlerAspect);
                } else {
                    publisherDepAspects.add(aspect);
                }
            }
        }
        return publisherDepAspects;
    }

    static class ForceJAXWSInvocationHandlerDeploymentAspect extends AbstractDeploymentAspect {
        public ForceJAXWSInvocationHandlerDeploymentAspect() {
        }
        @Override
        public void start(final Deployment dep) {
            for (final Endpoint ep : dep.getService().getEndpoints()) {
                ep.setInvocationHandler(new InvocationHandlerJAXWS());
            }
        }
    }

    private static ServiceContainer currentServiceContainer() {
        if(System.getSecurityManager() == null) {
            return CurrentServiceContainer.getServiceContainer();
        }
        return AccessController.doPrivileged(CurrentServiceContainer.GET_ACTION);
    }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2010, Red Hat Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt 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.jboss.as.weld;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.function.Consumer;
import java.util.function.Supplier;

import javax.enterprise.inject.spi.BeanManager;

import org.jboss.as.weld.deployment.BeanDeploymentArchiveImpl;
import org.jboss.as.weld.deployment.WeldDeployment;
import org.jboss.as.weld.logging.WeldLogger;
import org.jboss.as.weld.services.ModuleGroupSingletonProvider;
import org.jboss.msc.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StopContext;
import org.jboss.weld.Container;
import org.jboss.weld.ContainerState;
import org.jboss.weld.bootstrap.WeldBootstrap;
import org.jboss.weld.bootstrap.api.Environment;
import org.jboss.weld.bootstrap.spi.BeanDeploymentArchive;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.manager.api.ExecutorServices;
import org.jboss.weld.security.spi.SecurityServices;
import org.jboss.weld.transaction.spi.TransactionServices;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * Provides the initial bootstrap of the Weld container. This does not actually finish starting the container, merely gets it to
 * the point that the bean manager is available.
 *
 * @author Stuart Douglas
 * @author Richard Opalka
 */
public class WeldBootstrapService implements Service {

    public static final ServiceName SERVICE_NAME = ServiceName.of("WeldBootstrapService");

    private final WeldBootstrap bootstrap;
    private final WeldDeployment deployment;
    private final Environment environment;
    private final Map<String, BeanDeploymentArchive> beanDeploymentArchives;
    private final BeanDeploymentArchiveImpl rootBeanDeploymentArchive;

    private final String deploymentName;
    private final Consumer<WeldBootstrapService> weldBootstrapServiceConsumer;
    private final Supplier<ExecutorServices> executorServicesSupplier;
    private final Supplier<ExecutorService> serverExecutorSupplier;
    private final Supplier<SecurityServices> securityServicesSupplier;
    private final Supplier<TransactionServices> weldTransactionServicesSupplier;

    private volatile boolean started;

    public WeldBootstrapService(final WeldDeployment deployment, final Environment environment, final String deploymentName,
                                final Consumer<WeldBootstrapService> weldBootstrapServiceConsumer,
                                final Supplier<ExecutorServices> executorServicesSupplier,
                                final Supplier<ExecutorService> serverExecutorSupplier,
                                final Supplier<SecurityServices> securityServicesSupplier,
                                final Supplier<TransactionServices> weldTransactionServicesSupplier
    ) {
        this.deployment = deployment;
        this.environment = environment;
        this.deploymentName = deploymentName;
        this.weldBootstrapServiceConsumer = weldBootstrapServiceConsumer;
        this.executorServicesSupplier = executorServicesSupplier;
        this.serverExecutorSupplier = serverExecutorSupplier;
        this.securityServicesSupplier = securityServicesSupplier;
        this.weldTransactionServicesSupplier = weldTransactionServicesSupplier;
        this.bootstrap = new WeldBootstrap();
        Map<String, BeanDeploymentArchive> bdas = new HashMap<String, BeanDeploymentArchive>();
        BeanDeploymentArchiveImpl rootBeanDeploymentArchive = null;
        for (BeanDeploymentArchive archive : deployment.getBeanDeploymentArchives()) {
            bdas.put(archive.getId(), archive);
            if (archive instanceof BeanDeploymentArchiveImpl) {
                BeanDeploymentArchiveImpl bda = (BeanDeploymentArchiveImpl) archive;
                if (bda.isRoot()) {
                    rootBeanDeploymentArchive = bda;
                }
            }
        }
        this.rootBeanDeploymentArchive = rootBeanDeploymentArchive;
        this.beanDeploymentArchives = Collections.unmodifiableMap(bdas);
    }

    /**
     * Starts the weld container
     *
     * @throws IllegalStateException if the container is already running
     */
    public synchronized void start(final StartContext context) {
        if (started) {
            throw WeldLogger.ROOT_LOGGER.alreadyRunning("WeldContainer");
        }
        started = true;

        WeldLogger.DEPLOYMENT_LOGGER.startingWeldService(deploymentName);
        // set up injected services
        addWeldService(SecurityServices.class, securityServicesSupplier.get());

        TransactionServices transactionServices = weldTransactionServicesSupplier != null ? weldTransactionServicesSupplier.get() : null;
        if (transactionServices != null) {
            addWeldService(TransactionServices.class, transactionServices);
        }

        if (!deployment.getServices().contains(ExecutorServices.class)) {
            addWeldService(ExecutorServices.class, executorServicesSupplier.get());
        }

        ModuleGroupSingletonProvider.addClassLoaders(deployment.getModule().getClassLoader(),
                deployment.getSubDeploymentClassLoaders());

        ClassLoader oldTccl = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
        try {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(deployment.getModule().getClassLoader());
            bootstrap.startContainer(deploymentName, environment, deployment);
            WeldProvider.containerInitialized(Container.instance(deploymentName), getBeanManager(), deployment);
        } finally {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(oldTccl);
        }
        weldBootstrapServiceConsumer.accept(this);
    }

    /**
     * This is a no-op if {@link WeldStartService#start(StartContext)} completes normally and the shutdown is performed in
     * {@link WeldStartService#stop(org.jboss.msc.service.StopContext)}.
     */
    public synchronized void stop(final StopContext context) {
        weldBootstrapServiceConsumer.accept(null);
        if (started) {
            // WeldStartService#stop() not completed - attempt to perform the container cleanup
            final Container container = Container.instance(deploymentName);
            if (container != null && !ContainerState.SHUTDOWN.equals(container.getState())) {
                final ExecutorService executorService = serverExecutorSupplier.get();
                final Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        WeldLogger.DEPLOYMENT_LOGGER.debugf("Weld container cleanup for deployment %s", deploymentName);
                        ClassLoader oldTccl = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
                        try {
                            WildFlySecurityManager
                                    .setCurrentContextClassLoaderPrivileged(deployment.getModule().getClassLoader());
                            WeldProvider.containerShutDown(container);
                            container.setState(ContainerState.SHUTDOWN);
                            container.cleanup();
                            setStarted(false);
                        } finally {
                            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(oldTccl);
                            ModuleGroupSingletonProvider.removeClassLoader(deployment.getModule().getClassLoader());
                            context.complete();
                        }
                    }
                };
                try {
                    executorService.execute(task);
                } catch (RejectedExecutionException e) {
                    task.run();
                } finally {
                    context.asynchronous();
                }
            }
        }
    }

    /**
     * Gets the {@link BeanManager} for a given bean deployment archive id.
     *
     * @throws IllegalStateException if the container is not running
     * @throws IllegalArgumentException if the bean deployment archive id is not found
     */
    public BeanManagerImpl getBeanManager(String beanArchiveId) {
        if (!started) {
            throw WeldLogger.ROOT_LOGGER.notStarted("WeldContainer");
        }
        BeanDeploymentArchive beanDeploymentArchive = beanDeploymentArchives.get(beanArchiveId);
        if (beanDeploymentArchive == null) {
            throw WeldLogger.ROOT_LOGGER.beanDeploymentNotFound(beanArchiveId);
        }
        return bootstrap.getManager(beanDeploymentArchive);
    }

    /**
     * Adds a {@link Service} to the deployment. This method must not be called after the container has started
     */
    public <T extends org.jboss.weld.bootstrap.api.Service> void addWeldService(Class<T> type, T service) {
        deployment.addWeldService(type, service);
    }

    /**
     * Gets the {@link BeanManager} linked to the root bean deployment archive. This BeanManager has access to all beans in a
     * deployment
     *
     * @throws IllegalStateException if the container is not running
     */
    public BeanManagerImpl getBeanManager() {
        if (!started) {
            throw WeldLogger.ROOT_LOGGER.notStarted("WeldContainer");
        }
        return bootstrap.getManager(rootBeanDeploymentArchive);
    }

    /**
     * get all beans deployment archives in the deployment
     */
    public Set<BeanDeploymentArchive> getBeanDeploymentArchives() {
        return new HashSet<BeanDeploymentArchive>(beanDeploymentArchives.values());
    }

    public boolean isStarted() {
        return started;
    }

    void setStarted(boolean started) {
        this.started = started;
    }

    WeldDeployment getDeployment() {
        return deployment;
    }

    String getDeploymentName() {
        return deploymentName;
    }

    WeldBootstrap getBootstrap() {
        return bootstrap;
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2015, 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.jboss.as.webservices.deployers;

import java.util.HashMap;
import java.util.Map;

import org.jboss.wsf.spi.metadata.config.EndpointConfig;

/**
 * Defines mapping of endpoints and their config.
 *
 * @author Alessio Soldano
 */
public final class WSEndpointConfigMapping {

    private final Map<String, EndpointConfig> endpointConfigMap = new HashMap<String, EndpointConfig>();

    /**
     * Registers endpoint and its config.
     *
     * @param endpointClass WS endpoint
     * @param config Config with endpoint
     */
    public void registerEndpointConfig(final String endpointClass, final EndpointConfig config) {
        if ((endpointClass == null) || (config == null)) {
            throw new IllegalArgumentException();
        }
        endpointConfigMap.put(endpointClass, config);
    }

    /**
     * Returns config associated with WS endpoint.
     *
     * @param endpointClass to get associated config
     * @return associated config
     */
    public EndpointConfig getConfig(final String endpointClass) {
        return endpointConfigMap.get(endpointClass);
    }

    public boolean isEmpty() {
        return endpointConfigMap.size() == 0;
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright (c) 2011, 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.jboss.as.ejb3.component.messagedriven;

import java.util.Properties;

import javax.ejb.MessageDrivenBean;
import javax.ejb.TransactionManagementType;
import javax.resource.spi.ResourceAdapter;

import org.jboss.as.connector.util.ConnectorServices;
import org.jboss.as.ee.component.Attachments;
import org.jboss.as.ee.component.Component;
import org.jboss.as.ee.component.ComponentConfiguration;
import org.jboss.as.ee.component.ComponentConfigurator;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.DependencyConfigurator;
import org.jboss.as.ee.component.EEApplicationClasses;
import org.jboss.as.ee.component.ViewConfiguration;
import org.jboss.as.ee.component.ViewConfigurator;
import org.jboss.as.ee.component.ViewDescription;
import org.jboss.as.ee.component.interceptors.InterceptorClassDescription;
import org.jboss.as.ee.component.interceptors.InterceptorOrder;
import org.jboss.as.ee.component.interceptors.InvocationType;
import org.jboss.as.ee.metadata.MetadataCompleteMarker;
import org.jboss.as.ejb3.logging.EjbLogger;
import org.jboss.as.ejb3.component.EJBComponentDescription;
import org.jboss.as.ejb3.component.EJBViewDescription;
import org.jboss.as.ejb3.component.MethodIntf;
import org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor;
import org.jboss.as.ejb3.component.pool.PoolConfig;
import org.jboss.as.ejb3.component.pool.StrictMaxPoolConfigService;
import org.jboss.as.ejb3.deployment.EjbJarDescription;
import org.jboss.as.ejb3.tx.CMTTxInterceptor;
import org.jboss.as.ejb3.tx.EjbBMTInterceptor;
import org.jboss.as.ejb3.tx.LifecycleCMTTxInterceptor;
import org.jboss.as.ejb3.tx.TimerCMTTxInterceptor;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.reflect.ClassReflectionIndex;
import org.jboss.as.server.suspend.SuspendController;
import org.jboss.invocation.ImmediateInterceptorFactory;
import org.jboss.invocation.Interceptor;
import org.jboss.invocation.InterceptorContext;
import org.jboss.jca.core.spi.rar.ResourceAdapterRepository;
import org.jboss.metadata.ejb.spec.MessageDrivenBeanMetaData;
import org.jboss.modules.ModuleLoader;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceName;

/**
 * @author Carlo de Wolf
 * @author Richard Opalka
 */
public class MessageDrivenComponentDescription extends EJBComponentDescription {
    private final Properties activationProps;
    private String resourceAdapterName;
    private boolean deliveryActive;
    private String[] deliveryGroups;
    private boolean clusteredSingleton;
    private String mdbPoolConfigName;
    private final String messageListenerInterfaceName;
    private final boolean defaultMdbPoolAvailable;

    /**
     * Construct a new instance.
     *
     * @param componentName              the component name
     * @param componentClassName         the component instance class name
     * @param ejbJarDescription          the module description
     * @param defaultResourceAdapterName The default resource adapter name for this message driven bean. Cannot be null or empty string.
     */
    public MessageDrivenComponentDescription(final String componentName, final String componentClassName, final EjbJarDescription ejbJarDescription,
                                             final ServiceName deploymentUnitServiceName, final String messageListenerInterfaceName, final Properties activationProps,
                                             final String defaultResourceAdapterName, final MessageDrivenBeanMetaData descriptorData, final boolean defaultMdbPoolAvailable) {
        super(componentName, componentClassName, ejbJarDescription, deploymentUnitServiceName, descriptorData);
        if (messageListenerInterfaceName == null || messageListenerInterfaceName.isEmpty()) {
            throw EjbLogger.ROOT_LOGGER.stringParamCannotBeNullOrEmpty("Message listener interface");
        }
        if (defaultResourceAdapterName == null || defaultResourceAdapterName.trim().isEmpty()) {
            throw EjbLogger.ROOT_LOGGER.stringParamCannotBeNullOrEmpty("Default resource adapter name");
        }
        this.resourceAdapterName = defaultResourceAdapterName;
        this.deliveryActive = true;
        this.activationProps = activationProps;
        this.messageListenerInterfaceName = messageListenerInterfaceName;
        this.defaultMdbPoolAvailable = defaultMdbPoolAvailable;

        registerView(getEJBClassName(), MethodIntf.MESSAGE_ENDPOINT);
        // add the interceptor which will invoke the setMessageDrivenContext() method on a MDB which implements
        // MessageDrivenBean interface
        this.addSetMessageDrivenContextMethodInvocationInterceptor();
        getConfigurators().add(new ComponentConfigurator() {
            @Override
            public void configure(final DeploymentPhaseContext context, final ComponentDescription description, final ComponentConfiguration configuration) throws DeploymentUnitProcessingException {
                configuration.addTimeoutViewInterceptor(MessageDrivenComponentInstanceAssociatingFactory.instance(), InterceptorOrder.View.ASSOCIATING_INTERCEPTOR);
            }
        });
    }

    @Override
    public ComponentConfiguration createConfiguration(final ClassReflectionIndex classIndex, final ClassLoader moduleClassLoader, final ModuleLoader moduleLoader) {
        final ComponentConfiguration mdbComponentConfiguration = new ComponentConfiguration(this, classIndex, moduleClassLoader, moduleLoader);
        // setup the component create service
        final Class<?> messageListenerInterface;
        try {
            messageListenerInterface = Class.forName(getMessageListenerInterfaceName(), true, moduleClassLoader);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        mdbComponentConfiguration.setComponentCreateServiceFactory(new MessageDrivenComponentCreateServiceFactory(messageListenerInterface));

        // setup the configurator to inject the PoolConfig in the MessageDrivenComponentCreateService
        final MessageDrivenComponentDescription mdbComponentDescription = (MessageDrivenComponentDescription) mdbComponentConfiguration.getComponentDescription();
        mdbComponentConfiguration.getCreateDependencies().add(new PoolInjectingConfigurator(mdbComponentDescription));

        // setup the configurator to inject the resource adapter
        mdbComponentConfiguration.getCreateDependencies().add(new ResourceAdapterInjectingConfiguration());

        mdbComponentConfiguration.getCreateDependencies().add(new DependencyConfigurator<MessageDrivenComponentCreateService>() {
            @Override
            public void configureDependency(final ServiceBuilder<?> serviceBuilder, final MessageDrivenComponentCreateService mdbComponentCreateService) throws DeploymentUnitProcessingException {
                serviceBuilder.addDependency(SuspendController.SERVICE_NAME, SuspendController.class, mdbComponentCreateService.getSuspendControllerInjectedValue());
            }
        });

        // add the bmt interceptor
        if (TransactionManagementType.BEAN.equals(this.getTransactionManagementType())) {
            getConfigurators().add(new ComponentConfigurator() {
                @Override
                public void configure(final DeploymentPhaseContext context, final ComponentDescription description, final ComponentConfiguration configuration) throws DeploymentUnitProcessingException {

                    // add the bmt interceptor factory
                    configuration.addComponentInterceptor(EjbBMTInterceptor.FACTORY, InterceptorOrder.Component.BMT_TRANSACTION_INTERCEPTOR, false);
                }
            });
        } else {
            getConfigurators().add(new ComponentConfigurator() {
                @Override
                public void configure(final DeploymentPhaseContext context, final ComponentDescription description, final ComponentConfiguration configuration) throws DeploymentUnitProcessingException {
                    final EEApplicationClasses applicationClasses = context.getDeploymentUnit().getAttachment(Attachments.EE_APPLICATION_CLASSES_DESCRIPTION);
                    InterceptorClassDescription interceptorConfig = ComponentDescription.mergeInterceptorConfig(configuration.getComponentClass(), applicationClasses.getClassByName(description.getComponentClassName()), description, MetadataCompleteMarker.isMetadataComplete(context.getDeploymentUnit()));

                    configuration.addPostConstructInterceptor(new LifecycleCMTTxInterceptor.Factory(interceptorConfig.getPostConstruct(), true), InterceptorOrder.ComponentPostConstruct.TRANSACTION_INTERCEPTOR);
                    configuration.addPreDestroyInterceptor(new LifecycleCMTTxInterceptor.Factory(interceptorConfig.getPreDestroy(), true), InterceptorOrder.ComponentPreDestroy.TRANSACTION_INTERCEPTOR);

                    configuration.addTimeoutViewInterceptor(TimerCMTTxInterceptor.FACTORY, InterceptorOrder.View.CMT_TRANSACTION_INTERCEPTOR);
                }
            });
        }

        return mdbComponentConfiguration;
    }

    boolean isDefaultMdbPoolAvailable() {
        return defaultMdbPoolAvailable;
    }

    public Properties getActivationProps() {
        return activationProps;
    }

    public boolean isDeliveryActive() {
        return deliveryActive;
    }

    public void setDeliveryActive(boolean deliveryActive) {
        this.deliveryActive = deliveryActive;
    }

    public String[] getDeliveryGroups() {
        return deliveryGroups;
    }

    public void setDeliveryGroup(String[] groupNames) {
        this.deliveryGroups = groupNames;
    }

    public boolean isClusteredSingleton() {
        return clusteredSingleton;
    }

    public void setClusteredSingleton(boolean clusteredSingleton) {
        this.clusteredSingleton = clusteredSingleton;
    }

    public boolean isDeliveryControlled() {
        return deliveryGroups != null && deliveryGroups.length > 0 && deliveryGroups[0] != null || clusteredSingleton;
    }

    public ServiceName getDeliveryControllerName() {
        return getServiceName().append("DELIVERY");
    }

    public String getResourceAdapterName() {
        return resourceAdapterName;
    }

    public void setResourceAdapterName(String resourceAdapterName) {
        if (resourceAdapterName == null || resourceAdapterName.trim().isEmpty()) {
            throw EjbLogger.ROOT_LOGGER.stringParamCannotBeNullOrEmpty("Resource adapter name");
        }
        this.resourceAdapterName = resourceAdapterName;
    }

    @Override
    protected void setupViewInterceptors(EJBViewDescription view) {
        // let the super do its job
        super.setupViewInterceptors(view);

        view.getConfigurators().add(new ViewConfigurator() {
            @Override
            public void configure(DeploymentPhaseContext context, ComponentConfiguration componentConfiguration, ViewDescription description, ViewConfiguration configuration) throws DeploymentUnitProcessingException {

                //add the invocation type to the start of the chain
                //TODO: is there a cleaner way to do this?
                configuration.addViewInterceptor(new ImmediateInterceptorFactory(new Interceptor() {
                    @Override
                    public Object processInvocation(final InterceptorContext context) throws Exception {
                        context.putPrivateData(InvocationType.class, InvocationType.MESSAGE_DELIVERY);
                        return context.proceed();
                    }
                }), InterceptorOrder.View.INVOCATION_TYPE);

                // add the instance associating interceptor at the start of the interceptor chain
                configuration.addViewInterceptor(MessageDrivenComponentInstanceAssociatingFactory.instance(), InterceptorOrder.View.ASSOCIATING_INTERCEPTOR);

                final MessageDrivenComponentDescription mdb = (MessageDrivenComponentDescription) componentConfiguration.getComponentDescription();
                if (mdb.getTransactionManagementType() == TransactionManagementType.CONTAINER) {
                    configuration.addViewInterceptor(CMTTxInterceptor.FACTORY, InterceptorOrder.View.CMT_TRANSACTION_INTERCEPTOR);
                }
            }
        });

    }

    @Override
    protected void addCurrentInvocationContextFactory() {
        // add the current invocation context interceptor at the beginning of the component instance post construct chain
        this.getConfigurators().add(new ComponentConfigurator() {
            @Override
            public void configure(DeploymentPhaseContext context, ComponentDescription description, ComponentConfiguration configuration) throws DeploymentUnitProcessingException {
                configuration.addPostConstructInterceptor(CurrentInvocationContextInterceptor.FACTORY, InterceptorOrder.ComponentPostConstruct.EJB_SESSION_CONTEXT_INTERCEPTOR);
                configuration.addPreDestroyInterceptor(CurrentInvocationContextInterceptor.FACTORY, InterceptorOrder.ComponentPostConstruct.EJB_SESSION_CONTEXT_INTERCEPTOR);
            }
        });
    }

    @Override
    protected void addCurrentInvocationContextFactory(ViewDescription view) {
        view.getConfigurators().add(new ViewConfigurator() {
            @Override
            public void configure(DeploymentPhaseContext context, ComponentConfiguration componentConfiguration, ViewDescription description, ViewConfiguration configuration) throws DeploymentUnitProcessingException {
                configuration.addViewInterceptor(CurrentInvocationContextInterceptor.FACTORY, InterceptorOrder.View.INVOCATION_CONTEXT_INTERCEPTOR);
            }
        });
    }

    /**
     * Adds an interceptor to invoke the {@link MessageDrivenBean#setMessageDrivenContext(javax.ejb.MessageDrivenContext)}
     * if the MDB implements the {@link MessageDrivenBean} interface
     */
    private void addSetMessageDrivenContextMethodInvocationInterceptor() {
        // add the setMessageDrivenContext(MessageDrivenContext) method invocation interceptor for MDB
        // implementing the javax.ejb.MessageDrivenBean interface
        this.getConfigurators().add(new ComponentConfigurator() {
            @Override
            public void configure(DeploymentPhaseContext context, ComponentDescription description, ComponentConfiguration configuration) throws DeploymentUnitProcessingException {
                if (MessageDrivenBean.class.isAssignableFrom(configuration.getComponentClass())) {
                    configuration.addPostConstructInterceptor(new ImmediateInterceptorFactory(MessageDrivenBeanSetMessageDrivenContextInterceptor.INSTANCE), InterceptorOrder.ComponentPostConstruct.EJB_SET_CONTEXT_METHOD_INVOCATION_INTERCEPTOR);
                }
            }
        });
    }

    @Override
    public boolean isMessageDriven() {
        return true;
    }

    public void setPoolConfigName(final String mdbPoolConfigName) {
        this.mdbPoolConfigName = mdbPoolConfigName;
    }

    public String getPoolConfigName() {
        return this.mdbPoolConfigName;
    }

    private String getMessageListenerInterfaceName() {
        return messageListenerInterfaceName;
    }

    private static class PoolInjectingConfigurator implements DependencyConfigurator<Service<Component>> {

        private final MessageDrivenComponentDescription mdbComponentDescription;

        PoolInjectingConfigurator(final MessageDrivenComponentDescription mdbComponentDescription) {
            this.mdbComponentDescription = mdbComponentDescription;
        }

        @Override
        public void configureDependency(ServiceBuilder<?> serviceBuilder, Service<Component> service) {
            final MessageDrivenComponentCreateService mdbComponentCreateService = (MessageDrivenComponentCreateService) service;
            final String poolName = this.mdbComponentDescription.getPoolConfigName();
            // if no pool name has been explicitly set, then inject the *optional* "default mdb pool config"
            // If the default mdb pool config itself is not configured, then pooling is disabled for the bean
            if (poolName == null) {
                if (mdbComponentDescription.isDefaultMdbPoolAvailable()) {
                    serviceBuilder.addDependency(StrictMaxPoolConfigService.DEFAULT_MDB_POOL_CONFIG_SERVICE_NAME,
                            PoolConfig.class, mdbComponentCreateService.getPoolConfigInjector());
                }
            } else {
                // pool name has been explicitly set so the pool config is a required dependency
                serviceBuilder.addDependency(StrictMaxPoolConfigService.EJB_POOL_CONFIG_BASE_SERVICE_NAME.append(poolName),
                        PoolConfig.class, mdbComponentCreateService.getPoolConfigInjector());
            }
        }
    }

    /**
     * A dependency configurator which adds a dependency/injection into the {@link MessageDrivenComponentCreateService}
     * for the appropriate resource adapter service
     */
    private class ResourceAdapterInjectingConfiguration implements DependencyConfigurator<MessageDrivenComponentCreateService> {

        @Override
        public void configureDependency(ServiceBuilder<?> serviceBuilder, MessageDrivenComponentCreateService service) {
            final ServiceName raServiceName =
                ConnectorServices.getResourceAdapterServiceName(MessageDrivenComponentDescription.this.resourceAdapterName);
            // add the dependency on the RA service
            serviceBuilder.addDependency(ConnectorServices.RA_REPOSITORY_SERVICE, ResourceAdapterRepository.class, service.getResourceAdapterRepositoryInjector());
            serviceBuilder.addDependency(raServiceName, ResourceAdapter.class, service.getResourceAdapterInjector());
        }
    }

    @Override
    public boolean isTimerServiceApplicable() {
        return true;
    }

    @Override
    public MessageDrivenBeanMetaData getDescriptorData() {
        return (MessageDrivenBeanMetaData) super.getDescriptorData();
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, 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.eesecurity;

import java.util.List;
import java.util.function.Supplier;

import org.jboss.as.controller.Extension;
import org.jboss.as.controller.ExtensionContext;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.SubsystemRegistration;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.parsing.ExtensionParsingContext;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLElementReader;

/**
 * Domain extension used to initialize the EE Security subsystem.
 *
 * @author Stuart Douglas
 */
public class EESecurityExtension implements Extension {

    public static final String SUBSYSTEM_NAME = "ee-security";
    public static final String NAMESPACE = "urn:jboss:domain:ee-security:1.0";

    public static final ModelVersion MODEL_VERSION_1_0_0 = ModelVersion.create(1, 0, 0);

    private static final ModelVersion CURRENT_MODEL_VERSION = MODEL_VERSION_1_0_0;

    private static final EESecuritySubsystemParser_1_0 parser = new EESecuritySubsystemParser_1_0();

    private static final String RESOURCE_NAME = EESecurityExtension.class.getPackage().getName() + ".LocalDescriptions";
    static PathElement SUBSYSTEM_PATH = PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, SUBSYSTEM_NAME);

    static ResourceDescriptionResolver getResolver(final String... keyPrefix) {
        StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
        for (String kp : keyPrefix) {
            prefix.append('.').append(kp);
        }
        return new StandardResourceDescriptionResolver(prefix.toString(), RESOURCE_NAME, EESecurityExtension.class.getClassLoader(), true, false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void initialize(final ExtensionContext context) {
        final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, CURRENT_MODEL_VERSION);
        final ManagementResourceRegistration registration = subsystem.registerSubsystemModel(EESecuritySubsystemDefinition.INSTANCE);
        registration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
        subsystem.registerXMLElementWriter(parser);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void initializeParsers(final ExtensionParsingContext context) {
        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, EESecurityExtension.NAMESPACE, new Supplier<XMLElementReader<List<ModelNode>>>() {
            @Override
            public XMLElementReader<List<ModelNode>> get() {
                return parser;
            }
        });
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2009, Red Hat Middleware LLC, 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.jboss.as.ejb3.timerservice;

import java.util.Date;

import org.wildfly.security.manager.WildFlySecurityManager;

import static org.jboss.as.ejb3.logging.EjbLogger.EJB3_TIMER_LOGGER;

/**
 * A timer task which will be invoked at appropriate intervals based on a {@link javax.ejb.Timer}
 * schedule.
 *

 *

 * A {@link TimerTask} is responsible for invoking the timeout method on the target, through
 * the use of {@link org.jboss.as.ejb3.timerservice.spi.TimedObjectInvoker}
 *

 *

 * For calendar timers, this {@link TimerTask} is additionally responsible for creating and
 * scheduling the next round of timer task.
 *

 *
 * @author Jaikiran Pai
 * @author Wolf-Dieter Fink
 * @version $Revision: $
 */
public class TimerTask<T extends TimerImpl> implements Runnable {

    protected final String timedObjectId;
    protected final String timerId;

    /**
     * {@link org.jboss.as.ejb3.timerservice.TimerServiceImpl} to which this {@link TimerTask} belongs
     */
    protected final TimerServiceImpl timerService;

    private volatile boolean cancelled = false;

    /**
     * Creates a {@link TimerTask} for the timer
     *
     * @param timer The timer for which this task is being created.
     * @throws IllegalArgumentException If the passed timer is null
     */
    public TimerTask(T timer) {
        if (timer == null) {
            throw EJB3_TIMER_LOGGER.timerIsNull();
        }

        this.timedObjectId = timer.getTimedObjectId();
        timerId = timer.getId();
        timerService = timer.getTimerService();
    }

    /**
     * Invokes the timeout method through the {@link org.jboss.as.ejb3.timerservice.spi.TimedObjectInvoker} corresponding
     * to the {@link org.jboss.as.ejb3.timerservice.TimerImpl} to which this {@link TimerTask} belongs.
     *

     * This method also sets other attributes on the {@link org.jboss.as.ejb3.timerservice.TimerImpl} including the
     * next timeout of the timer and the timer state.
     *

     *

     * Additionally, for calendar timers, this method even schedules the next timeout timer task
     * before calling the timeout method for the current timeout.
     *

     */
    @Override
    public void run() {
        ClassLoader old = WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(getClass());
        try {
            final TimerImpl timer = timerService.getTimer(timerId);
            try {
                if (cancelled) {
                    EJB3_TIMER_LOGGER.debugf("Timer task was cancelled for %s", timer);
                    return;
                }

                Date now = new Date();
                EJB3_TIMER_LOGGER.debugf("Timer task invoked at: %s for timer %s", now, timer);

                //we lock the timer for this check, because if a cancel is in progress or a running timeout is about to finish
                //and try to update the timer state after finish the inTimeout method,
                //we do not want to do the isActive check, but wait for the other transaction to finish
                //one way or another
                timer.lock();

                try {
                    // If a retry thread is in progress, we don't want to allow another
                    // interval to execute until the retry is complete. See JIRA-1926.
                    if (timer.isInRetry()) {
                        EJB3_TIMER_LOGGER.skipInvokeTimeoutDuringRetry(timer, now);
                        // compute the next timeout, See JIRA AS7-2995.
                        timer.setNextTimeout(calculateNextTimeout(timer));
                        timerService.persistTimer(timer, false);
                        scheduleTimeoutIfRequired(timer);
                        return;
                    }

                    // Check whether the timer is running local
                    // If the recurring timer running longer than the interval is, we don't want to allow another
                    // execution until it is complete. See JIRA AS7-3119
                    if (timer.getState() == TimerState.IN_TIMEOUT || timer.getState() == TimerState.RETRY_TIMEOUT) {
                        EJB3_TIMER_LOGGER.skipOverlappingInvokeTimeout(timer, now);

                        if (EJB3_TIMER_LOGGER.isDebugEnabled()) {
                            // WFLY-10542 log thread stack trace which is processing timer task in debug level to diagnose timer overlap
                            Thread otherThread = timer.getExecutingThread();
                            // can be null for clustered timers if the timer is executing on another node.
                            if (otherThread != null) {
                                final StringBuilder debugMsg = new StringBuilder()
                                        .append("Thread: ")
                                        .append(otherThread.getName())
                                        .append(" Id: ")
                                        .append(otherThread.getId())
                                        .append(" of group ")
                                        .append(otherThread.getThreadGroup())
                                        .append(" is in state: ")
                                        .append(otherThread.getState());
                                for (StackTraceElement ste : otherThread.getStackTrace()) {
                                    debugMsg.append(System.lineSeparator()).append(ste.toString());
                                }
                                EJB3_TIMER_LOGGER.debugf(debugMsg.toString());
                            }
                        }

                        Date newD = this.calculateNextTimeout(timer);
                        timer.setNextTimeout(newD);
                        timerService.persistTimer(timer, false);
                        scheduleTimeoutIfRequired(timer);
                        return;
                    }

                    // Check whether we want to run the timer
                    if (!timerService.shouldRun(timer)) {
                        EJB3_TIMER_LOGGER.debugf("Skipping execution of timer for %s as it is being run on another node or the execution is suppressed by configuration", timer.getTimedObjectId());
                        timer.setNextTimeout(calculateNextTimeout(timer));
                        scheduleTimeoutIfRequired(timer);
                        return;
                    }

                    if (!timer.isActive()) {
                        EJB3_TIMER_LOGGER.debug("Timer is not active, skipping this scheduled execution at: " + now + "for " + timer);
                        return;
                    }
                    // set the current date as the "previous run" of the timer.
                    timer.setPreviousRun(new Date());
                    Date nextTimeout = this.calculateNextTimeout(timer);
                    timer.setNextTimeout(nextTimeout);
                    // change the state to mark it as in timeout method
                    timer.setTimerState(TimerState.IN_TIMEOUT, Thread.currentThread());

                    // persist changes
                    timerService.persistTimer(timer, false);

                } finally {
                    timer.unlock();
                }
                try {
                    // invoke timeout
                    this.callTimeout(timer);
                } catch (Exception e) {
                    EJB3_TIMER_LOGGER.errorInvokeTimeout(timer, e);
                    try {
                        EJB3_TIMER_LOGGER.timerRetried(timer);
                        retryTimeout(timer);
                    } catch (Exception retryException) {
                        // that's it, we can't do anything more. Let's just log the exception
                        // and return
                        EJB3_TIMER_LOGGER.errorDuringRetryTimeout(timer, retryException);
                    }
                } finally {
                    this.postTimeoutProcessing(timer);
                }
            } catch (Exception e) {
                EJB3_TIMER_LOGGER.exceptionRunningTimerTask(timer, timedObjectId, e);
            }
        } finally {
            WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(old);
        }
    }

    protected void scheduleTimeoutIfRequired(TimerImpl timer) {
    }

    protected void callTimeout(TimerImpl timer) throws Exception {
        invokeBeanMethod(timer);
    }

    protected void invokeBeanMethod(TimerImpl timer) throws Exception {
        timerService.getInvoker().callTimeout(timer);
    }

    protected Date calculateNextTimeout(TimerImpl timer) {
        long intervalDuration = timer.getInterval();
        if (intervalDuration > 0) {
            long now = new Date().getTime();
            long nextExpiration = timer.getNextExpiration().getTime();
            // compute skipped number of interval
            int periods = (int) ((now - nextExpiration) / intervalDuration);
            // compute the next timeout date
            return new Date(nextExpiration + (periods * intervalDuration) + intervalDuration);
        }
        return null;

    }

    /**
     * After a timeout failed the timer need to retried.
     * The method must lock the timer for state check and update but not during callTimeout run.
     *
     * @param timer timer to retry and state updates
     * @throws Exception
     */
    protected void retryTimeout(TimerImpl timer) throws Exception {
        boolean callTimeout = false;

        timer.lock();
        try {
            if (timer.isActive()) {
                EJB3_TIMER_LOGGER.retryingTimeout(timer);
                timer.setTimerState(TimerState.RETRY_TIMEOUT, Thread.currentThread());
                timerService.persistTimer(timer, false);
                callTimeout = true;
            } else {
                EJB3_TIMER_LOGGER.timerNotActive(timer);
            }
        } finally {
            timer.unlock();
        }
        if(callTimeout) {
            this.callTimeout(timer);
        }
    }

    /**
     * After running the timer calculate the new state or expire the timer and persist it if changed.
     * The method must lock the timer for state check and updates if overridden.
     *
     * @param timer timer to post processing and persist
     */
    protected void postTimeoutProcessing(TimerImpl timer) throws InterruptedException {
        timer.lock();
        try {
            TimerState timerState = timer.getState();
            if (timerState != TimerState.CANCELED
                    && timerState != TimerState.EXPIRED) {
                if (timer.getInterval() == 0) {
                    timerService.expireTimer(timer);
                } else {
                    timer.setTimerState(TimerState.ACTIVE, null);
                }
                timerService.persistTimer(timer, false);
            }
        } finally {
            timer.unlock();
        }
    }

    public void cancel() {
        cancelled = true;
    }
}


/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat Middleware LLC, 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.jboss.as.jsr77.subsystem;

import org.jboss.as.controller.Extension;
import org.jboss.as.controller.ExtensionContext;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.PersistentResourceXMLDescription;
import org.jboss.as.controller.PersistentResourceXMLParser;
import org.jboss.as.controller.ProcessType;
import org.jboss.as.controller.SubsystemRegistration;
import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.parsing.ExtensionParsingContext;
import org.jboss.as.controller.registry.ManagementResourceRegistration;

/**
 *
 * @author Kabir Khan
 */
public class JSR77ManagementExtension  implements Extension {

    public static final String NAMESPACE = "urn:jboss:domain:jsr77:1.0";
    public static final String SUBSYSTEM_NAME = "jsr77";
    private static final J2EEManagementSubsystemParser parser = new J2EEManagementSubsystemParser();
    private static final String RESOURCE_NAME = JSR77ManagementExtension.class.getPackage().getName() + ".LocalDescriptions";

    private static final ModelVersion CURRENT_MODEL_VERSION = ModelVersion.create(1, 0, 0);

    static ResourceDescriptionResolver getResourceDescriptionResolver(final String keyPrefix) {
        return new StandardResourceDescriptionResolver(keyPrefix, RESOURCE_NAME, JSR77ManagementExtension.class.getClassLoader(), true, true);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void initialize(ExtensionContext context) {
        final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, CURRENT_MODEL_VERSION);

        // Register the root subsystem resource.
        final ManagementResourceRegistration rootResource = subsystem.registerSubsystemModel(new JSR77ManagementRootResource(context.getProcessType() == ProcessType.APPLICATION_CLIENT));

        // Mandatory describe operation
        rootResource.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);

        subsystem.registerXMLElementWriter(parser);
    }

    /** {@inheritDoc} */
    @Override
    public void initializeParsers(ExtensionParsingContext context) {
        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, NAMESPACE, () -> parser);
    }

    static class J2EEManagementSubsystemParser extends PersistentResourceXMLParser {

        private static PersistentResourceXMLDescription xmlDescription;
        static {
            xmlDescription = PersistentResourceXMLDescription.builder(new JSR77ManagementRootResource(false), JSR77ManagementExtension.NAMESPACE)
                    .build();
        }
        @Override
        public PersistentResourceXMLDescription getParserDescription() {
            return xmlDescription;
        }
    }

}


/*
 * 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.jboss.as.connector.util;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;

/**
 * Privileged Blocks
 *
 * @author Jesper Pedersen
 */
class SecurityActions {
    /**
     * Constructor
     */
    private SecurityActions() {
    }

    /**
     * Get the declared methods
     *
     * @param c The class
     * @return The methods
     */
    static Method[] getDeclaredMethods(final Class<?> c) {
        if (System.getSecurityManager() == null)
            return c.getDeclaredMethods();

        return AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
            public Method[] run() {
                return c.getDeclaredMethods();
            }
        });
    }

    /**
     * Get the declared fields
     *
     * @param c The class
     * @return The fields
     */
    static Field[] getDeclaredFields(final Class<?> c) {
        if (System.getSecurityManager() == null)
            return c.getDeclaredFields();

        return AccessController.doPrivileged(new PrivilegedAction<Field[]>() {
            public Field[] run() {
                return c.getDeclaredFields();
            }
        });
    }

    /**
     * Set accessibleo
     *
     * @param ao The object
     */
    static void setAccessible(final AccessibleObject ao) {
        if (System.getSecurityManager() == null)
            ao.setAccessible(true);

        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            public Object run() {
                ao.setAccessible(true);
                return null;
            }
        });
    }

    /**
     * Get the constructor
     *
     * @param c      The class
     * @param params The parameters
     * @return The constructor
     * @throws NoSuchMethodException If a matching method is not found.
     */
    static Constructor<?> getConstructor(final Class<?> c, final Class<?>... params)
            throws NoSuchMethodException {
        if (System.getSecurityManager() == null)
            return c.getConstructor(params);

        Constructor<?> result = AccessController.doPrivileged(new PrivilegedAction<Constructor<?>>() {
            public Constructor<?> run() {
                try {
                    return c.getConstructor(params);
                } catch (NoSuchMethodException e) {
                    return null;
                }
            }
        });

        if (result != null)
            return result;

        throw new NoSuchMethodException();
    }

    /**
     * Get the method
     *
     * @param c      The class
     * @param name   The name
     * @param params The parameters
     * @return The method
     * @throws NoSuchMethodException If a matching method is not found.
     */
    static Method getMethod(final Class<?> c, final String name, final Class<?>... params)
            throws NoSuchMethodException {
        if (System.getSecurityManager() == null)
            return c.getMethod(name, params);

        Method result = AccessController.doPrivileged(new PrivilegedAction<Method>() {
            public Method run() {
                try {
                    return c.getMethod(name, params);
                } catch (NoSuchMethodException e) {
                    return null;
                }
            }
        });

        if (result != null)
            return result;

        throw new NoSuchMethodException();
    }
}