be.fgov.kszbcss.rhq.websphere.component.server.WebSphereServerComponent.java Source code

Java tutorial

Introduction

Here is the source code for be.fgov.kszbcss.rhq.websphere.component.server.WebSphereServerComponent.java

Source

/*
 * RHQ WebSphere Plug-in
 * Copyright (C) 2012-2014 Crossroads Bank for Social Security
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation, and/or the GNU Lesser
 * General Public License, version 2.1, also as published by the Free
 * Software Foundation.
 *
 * This program 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 General Public License and the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * and the GNU Lesser General Public License along with this program;
 * if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package be.fgov.kszbcss.rhq.websphere.component.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.management.JMException;
import javax.management.JMRuntimeException;
import javax.management.ObjectName;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.mc4j.ems.connection.EmsConnection;
import org.mc4j.ems.connection.settings.ConnectionSettings;
import org.mc4j.ems.connection.support.ConnectionProvider;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.configuration.ConfigurationFacet;
import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.core.pluginapi.event.EventContext;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;

import be.fgov.kszbcss.rhq.websphere.component.ConnectionFactoryJndiNamesQuery;
import be.fgov.kszbcss.rhq.websphere.component.ConnectionFactoryType;
import be.fgov.kszbcss.rhq.websphere.component.JAASAuthDataMap;
import be.fgov.kszbcss.rhq.websphere.component.JAASAuthDataQuery;
import be.fgov.kszbcss.rhq.websphere.component.WebSphereComponent;
import be.fgov.kszbcss.rhq.websphere.component.j2ee.DeployedApplicationsQuery;
import be.fgov.kszbcss.rhq.websphere.component.j2ee.SIBDestinationMap;
import be.fgov.kszbcss.rhq.websphere.component.j2ee.SIBDestinationMapQuery;
import be.fgov.kszbcss.rhq.websphere.component.j2ee.ejb.ActivationSpecQuery;
import be.fgov.kszbcss.rhq.websphere.component.j2ee.ejb.ActivationSpecs;
import be.fgov.kszbcss.rhq.websphere.component.pme.TimerManagerJndiNamesQuery;
import be.fgov.kszbcss.rhq.websphere.component.pme.WorkManagerJndiNamesQuery;
import be.fgov.kszbcss.rhq.websphere.component.server.log.J2EEComponentKey;
import be.fgov.kszbcss.rhq.websphere.component.server.log.LoggingProvider;
import be.fgov.kszbcss.rhq.websphere.component.server.log.none.NoneLoggingProvider;
import be.fgov.kszbcss.rhq.websphere.component.server.log.ras.RasLoggingProvider;
import be.fgov.kszbcss.rhq.websphere.component.server.log.xm4was.XM4WASLoggingProvider;
import be.fgov.kszbcss.rhq.websphere.component.sib.SIBMessagingEngineNamesQuery;
import be.fgov.kszbcss.rhq.websphere.config.ConfigData;
import be.fgov.kszbcss.rhq.websphere.config.ConfigObjectNotFoundException;
import be.fgov.kszbcss.rhq.websphere.config.ConfigQueryException;
import be.fgov.kszbcss.rhq.websphere.connector.ems.WebsphereConnectionProvider;
import be.fgov.kszbcss.rhq.websphere.process.ApplicationServer;
import be.fgov.kszbcss.rhq.websphere.process.ClusterNameQuery;
import be.fgov.kszbcss.rhq.websphere.process.ManagedServer;
import be.fgov.kszbcss.rhq.websphere.process.UnmanagedServer;
import be.fgov.kszbcss.rhq.websphere.proxy.EJBMonitor;
import be.fgov.kszbcss.rhq.websphere.proxy.J2CMessageEndpoint;
import be.fgov.kszbcss.rhq.websphere.proxy.Server;
import be.fgov.kszbcss.rhq.websphere.proxy.TraceService;
import be.fgov.kszbcss.rhq.websphere.proxy.WebSphereJVM;
import be.fgov.kszbcss.rhq.websphere.proxy.XM4WASJVM;
import be.fgov.kszbcss.rhq.websphere.support.measurement.JMXAttributeGroupHandler;
import be.fgov.kszbcss.rhq.websphere.support.measurement.MeasurementFacetSupport;

import com.ibm.websphere.management.AdminClient;
import com.ibm.websphere.management.exception.ConnectorException;

public final class WebSphereServerComponent extends WebSphereComponent<ResourceComponent<?>>
        implements MeasurementFacet, ConfigurationFacet, OperationFacet {
    private static final Logger log = LoggerFactory.getLogger(WebSphereServerComponent.class);

    private static final Map<String, Class<? extends LoggingProvider>> loggingProviderClasses;

    static {
        loggingProviderClasses = new HashMap<String, Class<? extends LoggingProvider>>();
        loggingProviderClasses.put("none", NoneLoggingProvider.class);
        loggingProviderClasses.put("ras", RasLoggingProvider.class);
        loggingProviderClasses.put("xm4was", XM4WASLoggingProvider.class);
    }

    private String cellName;
    private String nodeName;
    private String serverName;
    private File stateDir;
    private ApplicationServer server;
    private EmsConnection connection;
    private MeasurementFacetSupport measurementFacetSupport;
    private String loggingProviderName;
    private LoggingProvider loggingProvider;
    private WebSphereJVM wasJvm;
    private XM4WASJVM xm4wasJvm;
    private EJBMonitor ejbMonitor;

    private ConfigData<String> clusterName;
    private Map<ConnectionFactoryType, ConfigData<String[]>> connectionFactoryJndiNamesMap;
    private ConfigData<String[]> deployedApplications;
    private ConfigData<SIBDestinationMap> sibDestinationMap;
    private ConfigData<ActivationSpecs> activationSpecs;
    private ConfigData<JAASAuthDataMap> jaasAuthDataMap;
    private ConfigData<String[]> objectCacheInstanceNames;
    private ConfigData<String[]> workManagerJndiNames;
    private ConfigData<String[]> timerManagerJndiNames;
    private ConfigData<String[]> threadPoolNames;
    private ConfigData<String[]> sibMessagingEngineNames;

    @Override
    public void doStart() throws InvalidPluginConfigurationException {
        ResourceContext<ResourceComponent<?>> context = getResourceContext();

        Configuration pluginConfiguration = context.getPluginConfiguration();

        String[] parts = context.getResourceKey().split("/");
        cellName = parts[0];
        nodeName = parts[1];
        serverName = parts[2];
        stateDir = new File(
                new File(new File(new File(getResourceContext().getDataDirectory(), "state"), cellName), nodeName),
                serverName);

        initializeLoggingProvider(pluginConfiguration);

        PropertySimple unmanaged = pluginConfiguration.getSimple("unmanaged");
        if (unmanaged != null && unmanaged.getBooleanValue()) {
            server = new UnmanagedServer(cellName, nodeName, serverName, pluginConfiguration);
        } else {
            server = new ManagedServer(cellName, nodeName, serverName, pluginConfiguration);
        }
        server.init();

        measurementFacetSupport = new MeasurementFacetSupport(this);
        measurementFacetSupport.setDefaultHandler(new JMXAttributeGroupHandler(server.getServerMBean()));

        log.debug("Starting logging provider");
        loggingProvider.start(server, context.getEventContext(), EventPublisherImpl.INSTANCE, loadLoggingState());

        wasJvm = server.getMBeanClient("WebSphere:type=JVM,*").getProxy(WebSphereJVM.class);
        xm4wasJvm = server.getMBeanClient("XM4WAS:type=JVM,*").getProxy(XM4WASJVM.class);

        // TODO: the EJBMonitor MBean is not necessarily registered; maybe we need something to prevent the plug-in from querying the server repeatedly for the same MBean
        ejbMonitor = server.getMBeanClient("XM4WAS:type=EJBMonitor,*").getProxy(EJBMonitor.class);

        clusterName = registerConfigQuery(new ClusterNameQuery(nodeName, serverName));
        connectionFactoryJndiNamesMap = new HashMap<ConnectionFactoryType, ConfigData<String[]>>();
        for (ConnectionFactoryType type : ConnectionFactoryType.values()) {
            connectionFactoryJndiNamesMap.put(type,
                    registerConfigQuery(new ConnectionFactoryJndiNamesQuery(nodeName, serverName, type)));
        }
        deployedApplications = registerConfigQuery(new DeployedApplicationsQuery(nodeName, serverName));
        sibDestinationMap = registerConfigQuery(new SIBDestinationMapQuery(nodeName, serverName));
        activationSpecs = registerConfigQuery(new ActivationSpecQuery(nodeName, serverName));
        jaasAuthDataMap = registerConfigQuery(new JAASAuthDataQuery());
        objectCacheInstanceNames = registerConfigQuery(new ObjectCacheInstanceQuery(nodeName, serverName));
        workManagerJndiNames = registerConfigQuery(new WorkManagerJndiNamesQuery(nodeName, serverName));
        timerManagerJndiNames = registerConfigQuery(new TimerManagerJndiNamesQuery(nodeName, serverName));
        threadPoolNames = registerConfigQuery(new ThreadPoolNamesQuery(nodeName, serverName));
        sibMessagingEngineNames = registerConfigQuery(new SIBMessagingEngineNamesQuery(nodeName, serverName));
    }

    private void initializeLoggingProvider(Configuration pluginConfiguration) throws Error {
        loggingProviderName = pluginConfiguration.getSimpleValue("loggingProvider", "none");
        Class<? extends LoggingProvider> loggingProviderClass = loggingProviderClasses.get(loggingProviderName);
        if (loggingProviderClass == null) {
            throw new InvalidPluginConfigurationException("Unknown logging provider '" + loggingProviderName + "'");
        }
        if (log.isDebugEnabled()) {
            log.debug("Creating logging provider: name=" + loggingProviderName + ", class="
                    + loggingProviderClass.getName());
        }
        try {
            loggingProvider = loggingProviderClass.newInstance();
        } catch (Exception ex) {
            throw new Error("Failed to instantiate " + loggingProviderClass, ex);
        }
    }

    @Override
    public String getNodeName() {
        return nodeName;
    }

    @Override
    public String getServerName() {
        return serverName;
    }

    @Override
    public ApplicationServer getServer() {
        return server;
    }

    @Override
    public synchronized EmsConnection getEmsConnection() {
        if (connection == null) {
            Configuration pluginConfig = getResourceContext().getPluginConfiguration();
            ConnectionSettings connectionSettings = new ConnectionSettings();
            connectionSettings.setServerUrl(
                    pluginConfig.getSimpleValue("host", null) + ":" + pluginConfig.getSimpleValue("port", null));
            ConnectionProvider connectionProvider = new WebsphereConnectionProvider(server.getAdminClient());
            // The connection settings are not required to establish the connection, but they
            // will still be used in logging:
            connectionProvider.initialize(connectionSettings);
            connection = connectionProvider.connect();

            // If this is not present, then EmbeddedJMXServerDiscoveryComponent will fail to
            // discover the platform MXBeans.
            connection.loadSynchronous(false);
        }
        return connection;
    }

    public String[] getConnectionFactoryJndiNames(ConnectionFactoryType type)
            throws InterruptedException, ConnectorException, ConfigQueryException {
        return connectionFactoryJndiNamesMap.get(type).get();
    }

    public String[] getDeployedApplications()
            throws InterruptedException, ConnectorException, ConfigQueryException {
        return deployedApplications.get();
    }

    public SIBDestinationMap getSIBDestinationMap()
            throws InterruptedException, ConnectorException, ConfigQueryException {
        return sibDestinationMap.get();
    }

    public ActivationSpecs getActivationSpecs()
            throws InterruptedException, ConnectorException, ConfigQueryException {
        return activationSpecs.get();
    }

    public JAASAuthDataMap getJAASAuthDataMap()
            throws InterruptedException, ConnectorException, ConfigQueryException {
        return jaasAuthDataMap.get();
    }

    public String[] getObjectCacheInstanceNames()
            throws InterruptedException, ConnectorException, ConfigQueryException {
        return objectCacheInstanceNames.get();
    }

    public String[] getWorkManagerJndiNames()
            throws InterruptedException, ConnectorException, ConfigQueryException {
        return workManagerJndiNames.get();
    }

    public String[] getTimerManagerJndiNames()
            throws InterruptedException, ConnectorException, ConfigQueryException {
        return timerManagerJndiNames.get();
    }

    public String[] getThreadPoolNames() throws InterruptedException, ConnectorException, ConfigQueryException {
        return threadPoolNames.get();
    }

    public String[] getSIBMessagingEngineNames()
            throws InterruptedException, ConnectorException, ConfigQueryException {
        return sibMessagingEngineNames.get();
    }

    @Override
    protected boolean isConfigured() throws Exception {
        ApplicationServer server = getServer();
        try {
            // This effectively checks that the server still exists in the cell configuration.
            // Of course this will not always work:
            //  * If the server is unmanaged, then the cell configuration is managed by that
            //    server and we cannot really check if the server is still configured.
            //  * If the server is managed, then we rely on the deployment manager connection
            //    still being available after the server has been removed. This will not
            //    always be the case, in particular if the RHQ agent is restarted.
            clusterName.get();
            return true;
        } catch (ConfigObjectNotFoundException ex) {
            // We must return false only if the configuration object corresponding to the server
            // has not been found. That means that the type of exception specified in the catch
            // statement is really important.
            return false;
        }
    }

    @Override
    protected AvailabilityType doGetAvailability() {
        if (log.isDebugEnabled()) {
            log.debug("Starting to determine availability for server " + getResourceContext().getResourceKey());
        }
        AdminClient adminClient = server.getAdminClient();
        ObjectName serverMBean;
        try {
            serverMBean = adminClient.getServerMBean();
        } catch (ConnectorException ex) {
            log.debug("Unable to get server MBean => server DOWN", ex);
            return AvailabilityType.DOWN;
        }
        String state;
        try {
            state = (String) adminClient.getAttribute(serverMBean, "state");
        } catch (ConnectorException ex) {
            log.debug("Failed to get 'state' attribute from the server MBean => server DOWN", ex);
            return AvailabilityType.DOWN;
        } catch (JMException ex) {
            log.warn("Unexpected management exception while getting the 'state' attribute from the server MBean",
                    ex);
            return AvailabilityType.DOWN;
        } catch (JMRuntimeException ex) {
            log.warn(
                    "Unexpected management runtime exception while getting the 'state' attribute from the server MBean",
                    ex);
            return AvailabilityType.DOWN;
        }
        if (log.isDebugEnabled()) {
            log.debug("Server state = " + state);
        }
        if (state.equals("STARTED")) {
            return AvailabilityType.UP;
        } else {
            return AvailabilityType.DOWN;
        }
    }

    @Override
    public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> requests) throws Exception {
        measurementFacetSupport.getValues(report, requests);
    }

    @Override
    public OperationResult invokeOperation(String name, Configuration parameters)
            throws InterruptedException, Exception {
        if (name.equals("restart")) {
            Server server = getServer().getServerMBean().getProxy(Server.class);
            server.restart();
        } else if (name.equals("pauseAllMessageEndpoints")) {
            changeMessageEndpointState(true);
        } else if (name.equals("resumeAllMessageEndpoints")) {
            changeMessageEndpointState(false);
        } else if (name.equals("appendTraceString")) {
            TraceService traceService = getServer().getMBeanClient("WebSphere:type=TraceService,*")
                    .getProxy(TraceService.class);
            traceService.appendTraceString(parameters.getSimpleValue("traceString", null));
        } else if (name.equals("generateSystemDump")) {
            boolean performGC = Boolean.valueOf(parameters.getSimpleValue("performGC"));
            // Use WebSphere's MBean if possible (because XM4WAS may not be installed on the target server)
            if (performGC) {
                wasJvm.generateSystemDump();
            } else {
                xm4wasJvm.generateSystemDump(false);
            }
        }
        return null;
    }

    private OperationResult changeMessageEndpointState(boolean pause) throws Exception {
        AdminClient adminClient = getServer().getAdminClient();
        for (ObjectName endpointObjectName : adminClient
                .queryNames(new ObjectName("WebSphere:type=J2CMessageEndpoint,*"), null)) {
            J2CMessageEndpoint endpoint = getServer().getMBeanClient(endpointObjectName)
                    .getProxy(J2CMessageEndpoint.class);
            if (pause) {
                endpoint.pause();
            } else {
                endpoint.resume();
            }
        }
        return null;
    }

    @Override
    public Configuration loadResourceConfiguration() throws Exception {
        Configuration config = new Configuration();
        String clusterName = server.getClusterName();
        config.put(new PropertySimple("clusterName", clusterName));
        config.put(new PropertySimple("clusterKey",
                clusterName == null ? null : server.getCell() + "/" + clusterName));
        return config;
    }

    @Override
    public void updateResourceConfiguration(ConfigurationUpdateReport report) {
        throw new UnsupportedOperationException();
    }

    public void registerLogEventContext(String applicationName, String moduleName, String componentName,
            EventContext context) {
        loggingProvider.registerEventContext(new J2EEComponentKey(applicationName, moduleName, componentName),
                context);
    }

    public void unregisterLogEventContext(String applicationName, String moduleName, String componentName) {
        loggingProvider.unregisterEventContext(new J2EEComponentKey(applicationName, moduleName, componentName));
    }

    public EJBMonitor getEjbMonitor() {
        return ejbMonitor;
    }

    @Override
    protected void doStop() {
        persistLoggingState(loggingProvider.stop());
    }

    @Override
    protected void destroy() {
        server.destroy();
    }

    private void persistLoggingState(String state) {
        if (log.isDebugEnabled()) {
            log.debug("Persisting the state of the logging provider: " + state);
        }
        if (stateDir.exists() || stateDir.mkdirs()) {
            File stateFile = new File(stateDir, "logstate");
            if (state == null) {
                if (stateFile.exists()) {
                    if (log.isDebugEnabled()) {
                        log.debug("Deleting " + stateFile.getAbsolutePath());
                    }
                    if (!stateFile.delete()) {
                        log.error("Failed to delete " + stateFile.getAbsolutePath());
                    }
                } else {
                    log.debug(stateFile.getAbsolutePath() + " doesn't exist; not taking any action");
                }
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("Writing " + stateFile.getAbsolutePath());
                }
                try {
                    try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(stateFile),
                            "UTF-8")) {
                        writer.write(loggingProviderName);
                        writer.write(':');
                        writer.write(state);
                        writer.flush();
                    }
                } catch (IOException ex) {
                    log.error("Failed to write " + stateFile, ex);
                }
            }
        } else {
            log.error("Failed to create directory " + stateDir.getAbsolutePath());
        }
    }

    private String loadLoggingState() {
        log.debug("Loading persistent state of the logging provider");
        File stateFile = new File(stateDir, "logstate");
        if (stateFile.exists()) {
            try {
                if (log.isDebugEnabled()) {
                    log.debug("Reading " + stateDir.getAbsolutePath());
                }
                String content;
                try {
                    InputStream in = new FileInputStream(stateFile);
                    try {
                        content = IOUtils.toString(in, "UTF-8");
                    } finally {
                        in.close();
                    }
                } catch (IOException ex) {
                    log.error("Failed to read " + stateDir.getAbsolutePath(), ex);
                    return null;
                }
                int idx = content.indexOf(':');
                if (idx == -1) {
                    log.error("Unexpected content in file " + stateFile.getAbsolutePath());
                    return null;
                } else {
                    String previousProvider = content.substring(0, idx);
                    if (previousProvider.equals(loggingProviderName)) {
                        return content.substring(idx + 1);
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug("Previous logging provider (" + previousProvider
                                    + ") not the same as current provider (" + loggingProviderName + ")");
                        }
                        return null;
                    }
                }
            } finally {
                // We always delete the state file
                if (!stateFile.delete()) {
                    log.error("Unable to delete " + stateFile.getAbsolutePath());
                }
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug(stateDir.getAbsolutePath() + " doesn't exist; no persistent state available");
            }
            return null;
        }
    }
}