org.glassfish.admin.amx.impl.AMXStartupService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2006-2012 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.
*
* Portions Copyright [2018-2021] [Payara Foundation and/or its affiliates]
*/
package org.glassfish.admin.amx.impl;
import org.glassfish.admin.amx.base.DomainRoot;
import org.glassfish.admin.amx.base.MBeanTracker;
import org.glassfish.admin.amx.base.MBeanTrackerMBean;
import org.glassfish.admin.amx.base.SystemInfo;
import org.glassfish.admin.amx.core.proxy.ProxyFactory;
import org.glassfish.admin.amx.impl.mbean.ComplianceMonitor;
import org.glassfish.admin.amx.impl.mbean.DomainRootImpl;
import org.glassfish.admin.amx.impl.mbean.SystemInfoFactory;
import org.glassfish.admin.amx.impl.mbean.SystemInfoImpl;
import org.glassfish.admin.amx.impl.util.ImplUtil;
import org.glassfish.admin.amx.impl.util.InjectedValues;
import org.glassfish.admin.amx.impl.util.ObjectNameBuilder;
import org.glassfish.admin.amx.impl.util.SingletonEnforcer;
import org.glassfish.admin.amx.util.ExceptionUtil;
import org.glassfish.admin.amx.util.FeatureAvailability;
import org.glassfish.admin.amx.util.TimingDelta;
import org.glassfish.admin.amx.util.jmx.stringifier.StringifierRegistryIniter;
import org.glassfish.admin.amx.util.stringifier.StringifierRegistryImpl;
import org.glassfish.admin.amx.util.stringifier.StringifierRegistryIniterImpl;
import org.glassfish.admin.mbeanserver.AMXStartupServiceMBean;
import org.glassfish.api.amx.AMXLoader;
import org.glassfish.api.event.EventListener;
import org.glassfish.api.event.EventTypes;
import org.glassfish.api.event.Events;
import org.glassfish.external.amx.AMXGlassfish;
import org.glassfish.external.amx.AMXUtil;
import org.glassfish.external.amx.MBeanListener;
import org.glassfish.hk2.api.ServiceLocator;
import org.jvnet.hk2.annotations.Service;
import jakarta.inject.Inject;
import javax.management.*;
import javax.management.remote.JMXServiceURL;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.admin.amx.util.AMXLoggerInfo;
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.PreDestroy;
/**
* An {@link AMXLoader} responsible for loading core AMX MBeans
*/
@Service
public final class AMXStartupService implements PostConstruct, PreDestroy, AMXStartupServiceMBean {
@Inject
ServiceLocator mHabitat;
@Inject
InjectedValues mInjectedValues;
@Inject
private MBeanServer mMBeanServer;
@Inject
Events mEvents;
private volatile MBeanTracker mMBeanTracker;
private static final Logger logger = AMXLoggerInfo.getLogger();
public static MBeanTrackerMBean getMBeanTracker(final MBeanServer server) {
return MBeanServerInvocationHandler.newProxyInstance(server, MBeanTrackerMBean.MBEAN_TRACKER_OBJECT_NAME, MBeanTrackerMBean.class, false);
}
public AMXStartupService() {
new StringifierRegistryIniterImpl(StringifierRegistryImpl.DEFAULT);
new StringifierRegistryIniter(StringifierRegistryImpl.DEFAULT);
}
private final class ShutdownListener implements EventListener {
@Override
public void event(EventListener.Event event) {
if (event.is(EventTypes.SERVER_SHUTDOWN)) {
shutdown();
}
}
}
private void shutdown() {
logger.fine("AMX Startup Service: Shutting down AMX MBeans");
unloadAMXMBeans();
final ObjectName allAMXPattern = AMXUtil.newObjectName(AMXGlassfish.DEFAULT.amxJMXDomain(), "*");
final Set remainingAMX = mMBeanServer.queryNames(allAMXPattern, null);
if (!remainingAMX.isEmpty()) {
logger.log(Level.WARNING, AMXLoggerInfo.shutdownNotUnregistered, remainingAMX);
try {
Thread.sleep(1000);
} catch (final InterruptedException e) {
}
}
FeatureAvailability.getInstance().deRegisterFeatures();
logger.log(Level.INFO, "AMX Startup Service: AMX MBeans shutdown: {0}.", mMBeanServer.queryNames(allAMXPattern, null));
}
@Override
public void postConstruct() {
final TimingDelta delta = new TimingDelta();
SingletonEnforcer.register(this.getClass(), this);
if (mMBeanServer == null) {
throw new Error("AMX Startup Service: MBeanServer was null.");
}
try {
// StandardMBean is required because interface and class are in different packages
final StandardMBean mbean = new StandardMBean(this, AMXStartupServiceMBean.class);
mMBeanServer.registerMBean(mbean, OBJECT_NAME);
mMBeanTracker = new MBeanTracker(AMXGlassfish.DEFAULT.amxJMXDomain());
mMBeanTracker.setEmitMBeanStatus(false);
mMBeanServer.registerMBean(mMBeanTracker, MBeanTrackerMBean.MBEAN_TRACKER_OBJECT_NAME);
} catch (final Exception e) {
logger.log(Level.WARNING, "AMX Startup Service: Initialisation error.", e);
throw new Error(e);
}
logger.log(Level.INFO, "AMX Startup Service: Created in {0}ms. MBean: `{1}`.", new Object[]{delta.elapsedMillis(), OBJECT_NAME});
mEvents.register(new ShutdownListener());
}
@Override
public void preDestroy() {
logger.log(Level.INFO, "AMX Startup Service: Destroying service.");
unloadAMXMBeans();
}
@Override
public JMXServiceURL[] getJMXServiceURLs() {
try {
return (JMXServiceURL[]) mMBeanServer.getAttribute(AMXGlassfish.DEFAULT.getBootAMXMBeanObjectName(), "JMXServiceURLs");
} catch (final JMException e) {
throw new RuntimeException(e);
}
}
/**
* Return a proxy to the AMXStartupService.
*/
public static AMXStartupServiceMBean getAMXStartupServiceMBeanProxy(final MBeanServer mbs) {
AMXStartupServiceMBean ss = null;
if (mbs.isRegistered(OBJECT_NAME)) {
ss = AMXStartupServiceMBean.class.cast(
MBeanServerInvocationHandler.newProxyInstance(mbs, OBJECT_NAME, AMXStartupServiceMBean.class, false));
}
return ss;
}
@Override
public synchronized ObjectName getDomainRoot() {
try {
// might not be ready yet
return getDomainRootProxy().extra().objectName();
} catch (Exception e) {
// not there
}
return null;
}
DomainRoot getDomainRootProxy() {
return ProxyFactory.getInstance(mMBeanServer).getDomainRootProxy(false);
}
@Override
public ObjectName loadAMXMBeans() {
ObjectName objectName = AMXGlassfish.DEFAULT.domainRoot();
if (!mMBeanServer.isRegistered(objectName)) {
try {
objectName = _loadAMXMBeans();
} catch (final Exception e) {
logger.log(Level.SEVERE, "AMX Startup Service: Error loading AMX Beans.", e);
throw new RuntimeException(e);
}
}
return objectName;
}
/**
* also works as a loaded/not loaded flag: null if not yet loaded
*/
private volatile ObjectName DOMAIN_ROOT_OBJECTNAME = null;
private synchronized ObjectName loadDomainRoot() {
if (DOMAIN_ROOT_OBJECTNAME != null) {
return DOMAIN_ROOT_OBJECTNAME;
}
final DomainRootImpl domainRoot = new DomainRootImpl();
DOMAIN_ROOT_OBJECTNAME = AMXGlassfish.DEFAULT.domainRoot();
try {
DOMAIN_ROOT_OBJECTNAME = mMBeanServer.registerMBean(domainRoot, DOMAIN_ROOT_OBJECTNAME).getObjectName();
loadSystemInfo();
} catch (final Exception e) {
final Throwable rootCause = ExceptionUtil.getRootCause(e);
logger.log(Level.INFO, "AMX Startup Service: Error loading domain root.", rootCause);
throw new RuntimeException(rootCause);
}
return DOMAIN_ROOT_OBJECTNAME;
}
protected final ObjectName loadSystemInfo()
throws NotCompliantMBeanException, MBeanRegistrationException,
InstanceAlreadyExistsException {
final SystemInfoImpl systemInfo = SystemInfoFactory.createInstance(mMBeanServer);
ObjectName systemInfoObjectName
= ObjectNameBuilder.buildChildObjectName(mMBeanServer, DOMAIN_ROOT_OBJECTNAME, SystemInfo.class);
systemInfoObjectName = mMBeanServer.registerMBean(systemInfo, systemInfoObjectName).getObjectName();
return systemInfoObjectName;
}
/**
* run each AMXLoader in its own thread
*/
private static final class AMXLoaderThread extends Thread {
private final AMXLoader mLoader;
private volatile ObjectName mTop;
private final CountDownLatch mLatch;
public AMXLoaderThread(final AMXLoader loader) {
mLoader = loader;
mLatch = new CountDownLatch(1);
}
@Override
public void run() {
try {
logger.log(Level.FINE, "AMXStartupServiceNew.AMXLoaderThread: loading: {0}", mLoader.getClass().getName());
mTop = mLoader.loadAMXMBeans();
} catch (final Exception e) {
logger.log(Level.INFO, AMXLoggerInfo.failToLoad, e);
} finally {
mLatch.countDown();
}
}
public ObjectName waitDone() {
try {
mLatch.await();
} catch (InterruptedException e) {
}
return mTop;
}
public ObjectName top() {
return mTop;
}
}
class MyListener extends MBeanListener.CallbackImpl {
@Override
public void mbeanRegistered(final ObjectName objectName, final MBeanListener listener) {
super.mbeanRegistered(objectName, listener);
// verification code, nothing more to do
logger.log(Level.FINER, "MBean registered: {0}", objectName);
}
}
public synchronized ObjectName _loadAMXMBeans() {
// self-check important MBeans
final AMXGlassfish amxg = AMXGlassfish.DEFAULT;
final MBeanListener bootAMXListener = amxg.listenForBootAMX(mMBeanServer, new MyListener());
final MBeanListener domainRootListener = amxg.listenForDomainRoot(mMBeanServer, new MyListener());
// loads the high-level AMX MBeans, like DomainRoot, QueryMgr, etc
loadDomainRoot();
FeatureAvailability.getInstance().registerFeature(FeatureAvailability.AMX_CORE_READY_FEATURE, getDomainRoot());
logger.log(Level.FINE, "AMXStartupServiceNew: AMX core MBeans are ready for use, DomainRoot = {0}", getDomainRoot());
try {
// Find and load any additional AMX subsystems
final Collection loaders = mHabitat.getAllServices(AMXLoader.class);
logger.log(Level.FINE, "AMXStartupService._loadAMXMBeans(): found this many loaders: {0}", loaders.size());
final AMXLoaderThread[] threads = new AMXLoaderThread[loaders.size()];
int i = 0;
for (final AMXLoader loader : loaders) {
logger.log(Level.FINE, "AMXStartupService._loadAMXMBeans(): found this many loaders: {0}", loader);
threads[i] = new AMXLoaderThread(loader);
threads[i].start();
++i;
}
// don't mark AMX ready until all loaders have finished
for (final AMXLoaderThread thread : threads) {
thread.waitDone();
}
} catch (Throwable t) {
logger.log(Level.INFO, AMXLoggerInfo.fatalError, t);
} finally {
FeatureAvailability.getInstance().registerFeature(FeatureAvailability.AMX_READY_FEATURE, getDomainRoot());
logger.log(Level.INFO, AMXLoggerInfo.startupServiceDomainRoot, getDomainRoot());
}
// sanity-check (self-test) our listeners
if (bootAMXListener.getCallback().getRegistered() == null) {
throw new IllegalStateException("BootAMX listener was not called");
}
if (domainRootListener.getCallback().getRegistered() == null) {
throw new IllegalStateException("DomainRoot listener was not called");
}
return getDomainRoot();
}
@Override
public synchronized void unloadAMXMBeans() {
if (getDomainRoot() != null) {
final Collection loaders = mHabitat.getAllServices(AMXLoader.class);
for (final AMXLoader loader : loaders) {
if (loader == this) {
continue;
}
try {
loader.unloadAMXMBeans();
} catch (final Exception e) {
logger.log(Level.INFO, AMXLoggerInfo.failToUnLoad, e);
}
}
ImplUtil.unregisterAMXMBeans(getDomainRootProxy());
// Need to set this to null for making this work in the same VM.
this.DOMAIN_ROOT_OBJECTNAME = null;
// here is where we have to reset the singletons
ComplianceMonitor.removeInstance();
SystemInfoFactory.removeInstance();
}
}
// public Startup.Lifecycle getLifecycle() { return Startup.Lifecycle.SERVER; }
}