All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.admin.monitor.MonitoringBootstrap Maven / Gradle / Ivy

There is a newer version: 4.1.2.181
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.glassfish.admin.monitor;

import java.beans.PropertyChangeEvent;
import java.net.URISyntaxException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.external.probe.provider.StatsProviderInfo;
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.PreDestroy;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.runlevel.RunLevel;
import org.glassfish.external.probe.provider.StatsProviderManager;
import com.sun.enterprise.config.serverbeans.*;
import org.glassfish.flashlight.MonitoringRuntimeDataRegistry;

import org.jvnet.hk2.annotations.Optional;

import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.config.ConfigBeanProxy;
import org.jvnet.hk2.config.ConfigListener;
import org.jvnet.hk2.config.UnprocessedChangeEvents;

import com.sun.enterprise.module.Module;
import com.sun.enterprise.module.ModuleState;
import com.sun.enterprise.module.ModuleDefinition;
import com.sun.enterprise.module.ModulesRegistry;
import com.sun.enterprise.module.ModuleLifecycleListener;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.URI;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.StringTokenizer;

import org.glassfish.external.amx.AMXGlassfish;

import org.glassfish.api.event.Events;
import org.glassfish.api.event.EventListener;
import org.glassfish.api.event.EventTypes;
import org.glassfish.api.monitoring.ContainerMonitoring;
import org.glassfish.flashlight.client.ProbeClientMediator;
import org.glassfish.flashlight.impl.client.AgentAttacher;
import org.glassfish.flashlight.provider.ProbeProviderFactory;
import org.glassfish.flashlight.provider.ProbeProviderEventListener;
import org.glassfish.flashlight.provider.ProbeRegistry;
import org.glassfish.internal.api.InitRunLevel;
import org.glassfish.internal.api.LogManager;
import org.jvnet.hk2.config.Transactions;

import javax.inject.Inject;
import javax.inject.Named;
import static org.glassfish.admin.monitor.MLogger.*;


/**
 *
 * @author abbagani
 */
@Service
@RunLevel(InitRunLevel.VAL)
public class MonitoringBootstrap implements PostConstruct, PreDestroy, EventListener, ModuleLifecycleListener, ConfigListener {
    @SuppressWarnings("unused")
    @Inject @Optional
    private LogManager dependency0;  // The LogManager must come up prior to this service
    @Inject
    private MonitoringRuntimeDataRegistry mrdr;
    @Inject
    private ModulesRegistry registry;
    @Inject
    protected ProbeProviderFactory probeProviderFactory;
    @Inject
    protected ProbeClientMediator pcm;
    @Inject
    Events events;

    @Inject
    ServerEnvironment serverEnv;

    @Inject
    @Named(ServerEnvironment.DEFAULT_INSTANCE_NAME)
    @Optional
    MonitoringService monitoringService = null;
    @Inject
    private org.glassfish.flashlight.provider.ProbeRegistry probeRegistry;
    @Inject
    ServiceLocator habitat;
    @Inject
    Transactions transactions;

    //Don't inject ConfigBeans to avoid getting every event on them
    private Domain domain;


    Map map = Collections.synchronizedMap(new WeakHashMap());
    List appList = Collections.synchronizedList(new ArrayList());

    private static final String INSTALL_ROOT_URI_PROPERTY_NAME = "com.sun.aas.installRootURI";
    private static final Logger logger = getLogger();
    private static final String PROBE_PROVIDER_CLASS_NAMES = "probe-provider-class-names";
    private static final String PROBE_PROVIDER_XML_FILE_NAMES = "probe-provider-xml-file-names";
    private static final String DELIMITER = ",";
    private StatsProviderManagerDelegateImpl spmd;
    private boolean monitoringEnabled = false;
    private boolean hasDiscoveredXMLProviders = false;

    @Override
    public void postConstruct() {
        domain = habitat.getService(Domain.class);
        transactions.addListenerForType(ContainerMonitoring.class, this);
        transactions.addListenerForType(MonitoringService.class, this);
        transactions.addListenerForType(ModuleMonitoringLevels.class, this);

        // wbn: This value sticks for the life of the bootstrapping.  If the user changes it
        // somehow during bootstrapping we would have some problems so we just get the value
        // and run with it...

        boolean enableMonitoring = (monitoringService != null) ?
                Boolean.parseBoolean(monitoringService.getMonitoringEnabled())
                    && monitoringService.isAnyModuleOn()  :
                false;

        //Don't listen for any events and dont process any probeProviders or statsProviders (dont set delegate)
        if (enableMonitoring) {
            enableMonitoring(false);
        }
    }

    private void enableMonitoring(boolean isDiscoverXMLProbeProviders) {
        // Register as ModuleLifecycleListener
        events.register(this);

        enableMonitoringForProbeProviders(isDiscoverXMLProbeProviders);
        AgentAttacher.attachAgent();
        //Lets do the catch up for all the statsProviders (we might have ignored the module level changes earlier)
        if (spmd != null) {
            spmd.updateAllStatsProviders();
        }
        monitoringEnabled = true;
    }

    private void discoverProbeProviders() {
        // Iterate thru existing modules
        if (logger.isLoggable(Level.FINE))
            logger.log(Level.FINE, "Discovering the ProbeProviders");
        for (Module m : registry.getModules()) {
            if ((m.getState() == ModuleState.READY) || (m.getState() == ModuleState.RESOLVED)) {
                if (logger.isLoggable(Level.FINE))
                    logger.fine(" In (discoverProbeProviders) ModuleState - " + m.getState() + " : " + m.getName());
                verifyModule(m);
            }
        }

    }

    public void preDestroy() {
        //We need to do the cleanup for preventing errors from server starting in Embedded mode
        ProbeRegistry.cleanup();
        if (spmd != null) {
            spmd = new StatsProviderManagerDelegateImpl(pcm, probeRegistry, mrdr, domain, serverEnv.getInstanceName(),
                    monitoringService);
            StatsProviderManager.setStatsProviderManagerDelegate(spmd);
        }
    }

    public void event(Event event) {
        if (event.is(EventTypes.SERVER_READY)) {
            // Process the XMLProviders in lib/monitor dir. Should be the last thing to do in server startup.
            if (logger.isLoggable(Level.FINE))
                logger.log(Level.FINE, "Discovering the XML ProbeProviders from lib/monitor");
            discoverXMLProviders();
        }
    }

    public void setStatsProviderManagerDelegate() {
        // only run the code one time!
        if(spmd != null)
            return;

        //Set the StatsProviderManagerDelegate, so we can start processing the StatsProviders
        spmd = new StatsProviderManagerDelegateImpl(pcm, probeRegistry, mrdr, domain, serverEnv.getInstanceName(),
                monitoringService);
        StatsProviderManager.setStatsProviderManagerDelegate(spmd);
        StatsProviderUtil.setStatsProviderManagerDelegate(spmd);
        if (logger.isLoggable(Level.FINE))
            logger.fine(" StatsProviderManagerDelegate is assigned");

        // Register listener for AMX DomainRoot loaded
        final AMXGlassfish amxg = AMXGlassfish.DEFAULT;
        amxg.listenForDomainRoot(ManagementFactory.getPlatformMBeanServer(), spmd);
    }

    public void moduleResolved(Module module) {
        if (module == null) return;
        verifyModule(module);
    }

    public synchronized void moduleStarted(Module module) {
        if (module == null) return;
        verifyModule(module);
    }

    private synchronized void verifyModule(Module module) {
        if (module == null) return;
        String str = module.getName();
        if (!map.containsKey(str)) {
            map.put(str, module);
            addProvider(module);
        }
    }

    /**
     * An application that has probes can be registered.
     * @param appName application-name
     * @param appDir directory where application bits are present.
     * @param cl classloader that is used to load application files.
     */
    public synchronized void registerProbes(String appName, File appDir, ClassLoader cl) {
        if (appName == null) return;
        if (cl == null) {
            if (logger.isLoggable(Level.FINE)){
                logger.log(Level.FINE, "Null classloader passed for application : {0}", appName);
            }
            return;
        }
        if (!appList.contains(appName)) {
            appList.add(appName);
            addProvider(appDir, cl);
        }
    }

    // noop to satisfy interface
    @Override
    public synchronized void moduleStopped(Module module) {
    }

    // noop to satisfy interface
    @Override
    public void moduleInstalled(Module module) {
    }

    // noop to satisfy interface
    @Override
    public void moduleUpdated(Module module) {
    }

    private void addProvider(Module module) {
        if (logger.isLoggable(Level.FINE))
            logger.fine(" Adding the Provider - verified the module");
        ClassLoader mcl = module.getClassLoader();
        //get manifest entries and process
        ModuleDefinition md = module.getModuleDefinition();
        Manifest mf = null;
        if (md != null) {
            mf = md.getManifest();
        }
        if (mf != null) {
            processManifest(mf, mcl);
        }
        handleFutureStatsProviders();
    }

    private void addProvider(File appDir, ClassLoader classLoader) {
        //get manifest entries and process
        File manifestFile = new File(appDir, "META-INF" + File.separator + "MANIFEST.MF");
        String appDirPath = "";
        Manifest mf;
            try {
                appDirPath = appDir.getCanonicalPath();
                FileInputStream fis = new FileInputStream(manifestFile);
                mf = new Manifest(fis);
            } catch (IOException ex) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,"Can''t access "+"META-INF{0}" + "MANIFEST.MF" + " for {1}",
                            new Object[]{File.separator, appDirPath});
                    logger.fine(ex.getLocalizedMessage());
                }
                return;
            }
            processManifest(mf, classLoader);

        handleFutureStatsProviders();
    }

    private void processManifest(Manifest mf, ClassLoader mcl) {
        if (mf != null) {
            Attributes attrs = mf.getMainAttributes();
            String cnames = null;
            String xnames = null;
            if (attrs != null) {
                cnames = attrs.getValue(PROBE_PROVIDER_CLASS_NAMES);
                if (cnames != null) {
                    if (logger.isLoggable(Level.FINE))
                        logger.fine("probe providers = " + cnames);
                    StringTokenizer st = new StringTokenizer(cnames, DELIMITER);
                    while (st.hasMoreTokens()) {
                        try {
                            if (mcl != null)
                                processProbeProviderClass(mcl.loadClass(st.nextToken().trim()));
                        } catch (Exception e) {
                            logger.log(Level.SEVERE, unableToLoadProbeProvider, e);
                        }
                    }
                }
                xnames = attrs.getValue(PROBE_PROVIDER_XML_FILE_NAMES);
                if (xnames != null) {
                    if (logger.isLoggable(Level.FINE))
                        logger.fine("xnames = " + xnames);
                    StringTokenizer st = new StringTokenizer(xnames, DELIMITER);
                    while (st.hasMoreTokens()) {
                        processProbeProviderXML(mcl, st.nextToken(), true);
                    }
                }
            }
        }
    }

    public void handleFutureStatsProviders() {
        // we just registered a Probe Provider
        // If there are any future items -- let's try to register them again.

        if(FutureStatsProviders.isEmpty())
            return; // Performance note -- this should be the case almost always

        List removeList = new ArrayList();
        Iterator it = FutureStatsProviders.iterator();

        // the iterator does not allow the remove operation - thus the complexity!
        while(it.hasNext()) {
            StatsProviderInfo spInfo = it.next();
            try {
                spmd.tryToRegister(spInfo);
                removeList.add(spInfo);
            }
            catch(RuntimeException re) {
                // no probe registered yet...
            }
        }

        for(StatsProviderInfo spInfo : removeList) {
            FutureStatsProviders.remove(spInfo);
        }
    }

    private void discoverXMLProviders() {
        // Dont process if already discovered, Ideally we should do this whenever a new XML is dropped in lib/monitor
        if (hasDiscoveredXMLProviders)
            return;

        try {
            URI xmlProviderDirStr = new URI(System.getProperty(INSTALL_ROOT_URI_PROPERTY_NAME) + "/" + "lib" + "/" + "monitor");
            if (logger.isLoggable(Level.FINE))
                logger.fine("ProviderXML's Dir = " + xmlProviderDirStr.getPath());
            File xmlProviderDir = new File(xmlProviderDirStr.getPath());
            //File scriptFile = new File ("/space/GFV3_BLD/glassfish/domains/domain1/applications/scripts/InvokeJavaFromJavascript.js");
            if (logger.isLoggable(Level.FINE)) {
                logger.fine("ProviderXML's Dir exists = " + xmlProviderDir.exists());
                logger.fine("ProviderXML's Dir path - " + xmlProviderDir.getAbsolutePath());
            }
            loadXMLProviders(xmlProviderDir);
            hasDiscoveredXMLProviders = true;
        } catch (URISyntaxException ex) {
            logger.log(Level.SEVERE, unableToProcessXMLProbeProvider, ex);
        }
    }

    private void loadXMLProviders(File xmlProvidersDir) {
        // Creates a filter which will return only xml files
        FilenameFilter filter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(".xml");
            }
        };
        // Retrieves all the provider XML's
        File[] files = xmlProvidersDir.listFiles(filter);
        if (files == null)
            return;
        Map providerMap = new HashMap();

        for (File file : files) {
            if (logger.isLoggable(Level.FINE))
                logger.fine("Found the provider xml - " + file.getAbsolutePath());
            int index = file.getName().indexOf("-:");
            if (index != -1) {
                String moduleName = file.getName().substring(0,index);
                providerMap.put(moduleName, file);
                if (logger.isLoggable(Level.FINE))
                    logger.fine(" The provider xml belongs to - \"" + moduleName + "\"");
                if (!map.containsKey(moduleName)) {
                    continue;
                }
                if (logger.isLoggable(Level.FINE))
                    logger.fine (" Module found (containsKey)");
                Module module = map.get(moduleName);

                if (module == null) {
                    logger.log(Level.SEVERE,
                                monitoringMissingModuleFromXmlProbeProviders,
                                        new Object[] {moduleName});
                } else {
                    ClassLoader mcl = module.getClassLoader();

                    if (logger.isLoggable(Level.FINE)) {
                        logger.fine("ModuleClassLoader = " + mcl);
                        logger.fine("XML File path = " + file.getAbsolutePath());
                    }
                    processProbeProviderXML(mcl, file.getAbsolutePath(), false);
                }
            }
        }
    }

    private void processProbeProviderClass(Class cls) {
        if (logger.isLoggable(Level.FINE))
            logger.fine("processProbeProviderClass for " + cls);
        try {

            probeProviderFactory.getProbeProvider(cls);

        } catch (InstantiationException ex) {
            logger.log(Level.SEVERE, unableToLoadProbeProvider, ex);
        } catch (IllegalAccessException ex) {
            logger.log(Level.SEVERE, unableToLoadProbeProvider, ex);
        }
    }

    private void processProbeProviderXML(ClassLoader mcl, String xname, boolean inBundle) {
        probeProviderFactory.processXMLProbeProviders(mcl, xname, inBundle);
    }

    /*public void event(Event event) {
        if (event.name().equals(EventTypes.PREPARE_SHUTDOWN_NAME)) {
            spmd.unregisterAll();
        }
    }*/

    public UnprocessedChangeEvents changed(PropertyChangeEvent[] propertyChangeEvents) {
        if (logger.isLoggable(Level.FINE))
            logger.fine(" spmd = " + spmd);
        StatsProviderRegistry spr = (spmd == null) ? null : spmd.getStatsProviderRegistry();
        if (logger.isLoggable(Level.FINE))
            logger.fine("spr = " + spr);
        for (PropertyChangeEvent event : propertyChangeEvents) {
            // let's get out of here ASAP if it is not our stuff!!
            if(event == null)
                continue;

            if (!isCurrentInstanceMatchingTarget(event)) {
                continue;
            }
            
            String propName = event.getPropertyName();
            Object oldVal = event.getOldValue();
            Object newVal = event.getNewValue();

            if(newVal == null || newVal.equals(oldVal))
                continue;   // no change!!

            if(!ok(propName))
                continue;
            String level_change_mesg = "Level change event received, {0} New Level = {1}, Old Level = {2}";
            if (event.getSource() instanceof ModuleMonitoringLevels) {
                String newEnabled = newVal.toString().toUpperCase(Locale.ENGLISH);
                String oldEnabled = (oldVal == null) ? "OFF" : oldVal.toString().toUpperCase(Locale.ENGLISH);
                if (logger.isLoggable(Level.FINE))
                    logger.log(Level.FINE, level_change_mesg,
                                new Object[]{propName, newEnabled, oldEnabled});
                if (!newEnabled.equals(oldEnabled)) {
                    handleLevelChange(propName, newEnabled);
                }
            }
            else if (event.getSource() instanceof ContainerMonitoring) {
                ContainerMonitoring cm = (ContainerMonitoring)event.getSource();

                String newEnabled = newVal.toString().toUpperCase(Locale.ENGLISH);
                String oldEnabled = (oldVal == null) ? "OFF" : oldVal.toString().toUpperCase(Locale.ENGLISH);
                if (logger.isLoggable(Level.FINE))
                    logger.log(Level.FINE, level_change_mesg,
                                new Object[]{propName, newEnabled, oldEnabled});
                if (!newEnabled.equals(oldEnabled)) {
                    handleLevelChange(cm.getName(), newEnabled);
                }
            }
            else if(event.getSource() instanceof MonitoringService) {
                // we don't want to get fooled because config allows ANY string.
                // e.g. "false" --> "foo" --> "fals" are all NOT changes!
                // so we convert to boolean and then compare...
                boolean newEnabled = Boolean.parseBoolean(newVal.toString());
                boolean oldEnabled = (oldVal == null) ? !newEnabled : Boolean.parseBoolean(oldVal.toString());
                if (logger.isLoggable(Level.FINE))
                    logger.log(Level.FINE, level_change_mesg,
                                new Object[]{propName, newEnabled, oldEnabled});

                if(newEnabled != oldEnabled) {
                    handleServiceChange(spr, propName, newEnabled);
                }
            }
        }

       return null;
    }

    private boolean isCurrentInstanceMatchingTarget(PropertyChangeEvent event) {
        // DAS receive all the events, so we need to figure out 
        // whether we should take action on DAS depending on the event.

        if(serverEnv.isInstance()) {
            return true;
        } 

        ConfigBeanProxy proxy = (ConfigBeanProxy)(event.getSource());
        while(proxy != null && !(proxy instanceof Config)) {
            proxy = proxy.getParent();
        }
        if (proxy != null) {
            Config config = (Config)proxy;
            return config.isDas();
        }

        return false;
    }
    
    private void handleLevelChange(String propName, String enabledStr) {
        if (logger.isLoggable(Level.FINE))
            logger.fine("In handleLevelChange(), spmd = " + spmd + "  Enabled="+enabledStr);
        if(!ok(propName))
            return;

        if (!monitoringEnabled && !"OFF".equals(enabledStr)) {
            enableMonitoring(true);
        }

        if(spmd == null)
            return; // nothing to do!

        if (parseLevelsBoolean(enabledStr)) {
            if (logger.isLoggable(Level.FINE))
                logger.log(Level.FINE,
                        "Enabling {0} monitoring to {1}", new Object[] {propName, enabledStr});
            try {
                spmd.enableStatsProviders(propName);
            } catch(RuntimeException rte) {
                logger.log(Level.INFO, UNHANDLED_EXCEPTION_INFO, rte);
            }
        } else {
            if (logger.isLoggable(Level.FINE))
                logger.log(Level.FINE,
                        "Disabling {0} monitoring", propName);
            spmd.disableStatsProviders(propName);
        }
    }

    private void handleServiceChange(StatsProviderRegistry spr, String propName, boolean enabled) {
        if(!ok(propName))
            return;

        if (propName.equals("mbean-enabled")) {
            if(spr == null) // required!
                return;

            if(enabled) {
                logger.log(Level.INFO, mbeanEnabled);
                spmd.registerAllGmbal();
            } else {
                logger.log(Level.INFO, mbeanDisabled);
                spmd.unregisterAllGmbal();
            }
        }
        else if(propName.equals("dtrace-enabled")) {
            logger.log(Level.INFO,dtraceEnabled);
            probeProviderFactory.dtraceEnabledChanged(enabled);
        }
        else if(propName.equals("monitoring-enabled")) {
            //This we do it so we can (un)expose probes as DTrace
            probeProviderFactory.monitoringEnabledChanged(enabled);

            if(enabled) {
                logger.log(Level.INFO,monitoringEnabledLogMsg);
                enableMonitoring(true);
            } else { // if disabled
                logger.log(Level.INFO,monitoringDisabledLogMsg);
                disableMonitoringForProbeProviders();
                if (spmd != null) {
                    spmd.disableAllStatsProviders();
                }
            }
        }
    }

    private void enableMonitoringForProbeProviders(boolean isDiscoverXMLProviders) {
        //Process all ProbeProviders from modules loaded
        discoverProbeProviders();
        //Start listening to any new Modules that are coming in now
        registry.register(this);
        //Don't do this the first time, since we need to wait till the server starts
        // We should try to do this in a seperate thread, as we dont want to get held up in server start
        if (isDiscoverXMLProviders) {
            //Process all XMLProbeProviders from lib/monitor directory
            discoverXMLProviders();
        }
        //Start registering the cached StatsProviders and also those that are coming in new
        setStatsProviderManagerDelegate();
        //register probeprocee listener
        probeProviderFactory.addProbeProviderEventListener(new ProcessProbes());
    }

    private void disableMonitoringForProbeProviders() {
        //Cannot do a whole lot here. The providers that are registered will remain registered.
        //Just disable the StatsProviders, so you remove the listening overhead
        registry.unregister(this);
    }

    private boolean ok(String s) {
        return s != null && s.length() > 0;
    }

    private boolean parseLevelsBoolean(String s) {
        if (ok(s) && s.equals("OFF"))
            return false;

        return true;
    }

    private class ProcessProbes implements ProbeProviderEventListener {
        public  void probeProviderAdded(String moduleProviderName, String moduleName,
                String probeProviderName, String invokerId, Class providerClazz, T provider) {
            handleFutureStatsProviders();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy