| /******************************************************************************* |
| * Copyright (c) 2008, 2010 VMware Inc. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * VMware Inc. - initial contribution |
| *******************************************************************************/ |
| |
| package org.eclipse.virgo.nano.core.internal; |
| |
| import java.lang.management.ManagementFactory; |
| import java.util.Map; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.management.JMException; |
| import javax.management.MBeanServer; |
| import javax.management.ObjectInstance; |
| import javax.management.ObjectName; |
| |
| import org.eclipse.virgo.kernel.config.internal.KernelConfiguration; |
| import org.eclipse.virgo.nano.core.BlockingAbortableSignal; |
| import org.eclipse.virgo.nano.core.BundleUtils; |
| import org.eclipse.virgo.nano.core.FailureSignalledException; |
| import org.eclipse.virgo.nano.core.FatalKernelException; |
| import org.eclipse.virgo.nano.core.Shutdown; |
| import org.eclipse.virgo.kernel.diagnostics.KernelLogEvents; |
| import org.eclipse.virgo.medic.dump.DumpGenerator; |
| import org.eclipse.virgo.medic.eventlog.EventLogger; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.service.event.Event; |
| import org.osgi.service.event.EventAdmin; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * <code>StartupTracker</code> tracks the startup of the Kernel and produces event log entries, and |
| * {@link EventAdmin} events as the kernel starts. |
| * <p /> |
| * |
| * <strong>Concurrent Semantics</strong><br /> |
| * |
| * Thread-safe. |
| * |
| */ |
| final class StartupTracker { |
| |
| private static final String THREAD_NAME_STARTUP_TRACKER = "startup-tracker"; |
| |
| static final String APPLICATION_CONTEXT_FILTER = "(objectClass=org.springframework.context.ApplicationContext)"; |
| |
| private static final String KERNEL_EVENT_TOPIC = "org/eclipse/virgo/kernel/"; |
| |
| private static final String KERNEL_EVENT_STARTING = KERNEL_EVENT_TOPIC + "STARTING"; |
| |
| private static final String KERNEL_EVENT_STARTED = KERNEL_EVENT_TOPIC + "STARTED"; |
| |
| private static final String KERNEL_EVENT_START_TIMED_OUT = KERNEL_EVENT_TOPIC + "START_TIMED_OUT"; |
| |
| private static final String KERNEL_EVENT_START_ABORTED = KERNEL_EVENT_TOPIC + "START_ABORTED"; |
| |
| private static final String KERNEL_EVENT_START_FAILED = KERNEL_EVENT_TOPIC + "START_FAILED"; |
| |
| private static final String KERNEL_BSN_PREFIX = "org.eclipse.virgo.kernel"; |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(StartupTracker.class); |
| |
| private final KernelStatus status = new KernelStatus(); |
| |
| private final KernelConfiguration configuration; |
| |
| private final Thread startupTrackingThread; |
| |
| private volatile ObjectInstance statusInstance; |
| |
| StartupTracker(BundleContext context, KernelConfiguration configuration, int startupWaitTime, BundleStartTracker asyncBundleStartTracker, Shutdown shutdown, DumpGenerator dumpGenerator) { |
| Runnable startupTrackingRunnable = new StartupTrackingRunnable(context, startupWaitTime, asyncBundleStartTracker, this.status, shutdown, dumpGenerator); |
| this.startupTrackingThread = new Thread(startupTrackingRunnable, THREAD_NAME_STARTUP_TRACKER); |
| this.configuration = configuration; |
| } |
| |
| void start() { |
| registerKernelStatusMBean(); |
| this.startupTrackingThread.start(); |
| } |
| |
| void stop() { |
| unregisterKernelStatusMBean(); |
| } |
| |
| private void registerKernelStatusMBean() { |
| MBeanServer server = ManagementFactory.getPlatformMBeanServer(); |
| try { |
| ObjectName name = ObjectName.getInstance(StartupTracker.this.configuration.getDomain(), "type", "KernelStatus"); |
| this.statusInstance = server.registerMBean(this.status, name); |
| } catch (JMException e) { |
| throw new FatalKernelException("Unable to register KernelStatus MBean", e); |
| } |
| } |
| |
| private void unregisterKernelStatusMBean() { |
| MBeanServer server = ManagementFactory.getPlatformMBeanServer(); |
| try { |
| ObjectInstance instance = this.statusInstance; |
| if (instance != null && server.isRegistered(instance.getObjectName())) { |
| server.unregisterMBean(this.statusInstance.getObjectName()); |
| } |
| } catch (JMException e) { |
| throw new FatalKernelException("Unable to unregister KernelStatus MBean", e); |
| } |
| } |
| |
| private final static class StartupTrackingRunnable implements Runnable { |
| |
| private final BundleContext context; |
| |
| private final int startupWaitTime; |
| |
| private final BundleStartTracker asyncBundleStartTracker; |
| |
| private final KernelStatus kernelStatus; |
| private final Shutdown shutdown; |
| private final DumpGenerator dumpGenerator; |
| |
| private final ServiceReferenceTracker serviceReferenceTracker; |
| |
| private EventLogger eventLogger = null; |
| private EventAdmin eventAdmin = null; |
| |
| private StartupTrackingRunnable(BundleContext context, int startupWaitTime, BundleStartTracker asyncBundleStartTracker, KernelStatus kernelStatus, Shutdown shutdown, DumpGenerator dumpGenerator) { |
| this.context = context; |
| this.startupWaitTime = startupWaitTime; |
| this.asyncBundleStartTracker = asyncBundleStartTracker; |
| this.kernelStatus = kernelStatus; |
| this.shutdown = shutdown; |
| this.dumpGenerator = dumpGenerator; |
| this.serviceReferenceTracker = new ServiceReferenceTracker(context); |
| } |
| |
| public void run() { |
| this.eventLogger = getEventLoggerService(); |
| this.eventAdmin = getEventAdminService(); |
| |
| try { |
| kernelStarting(); |
| |
| Bundle[] bundles = this.context.getBundles(); |
| |
| try { |
| long waitTime = TimeUnit.SECONDS.toMillis(this.startupWaitTime); |
| |
| for (Bundle bundle : bundles) { |
| |
| if (!BundleUtils.isFragmentBundle(bundle) && isKernelBundle(bundle)) { |
| BlockingAbortableSignal signal = new BlockingAbortableSignal(); |
| |
| this.asyncBundleStartTracker.trackStart(bundle, signal); |
| |
| LOGGER.debug("Awaiting startup of bundle {} for up to {} milliseconds with signal {}.", new Object[]{bundle, waitTime, signal}); |
| |
| long startTime = System.currentTimeMillis(); |
| boolean bundleStarted = signal.awaitCompletion(waitTime, TimeUnit.MILLISECONDS); |
| waitTime -= System.currentTimeMillis() - startTime; |
| |
| if (!bundleStarted) { |
| if(signal.isAborted()){ |
| LOGGER.error("Bundle {} aborted before the Kernel timeout of {} seconds with {} seconds remaining.", new Object[]{bundle, this.startupWaitTime, TimeUnit.MILLISECONDS.toSeconds(waitTime)}); |
| kernelStartAborted(bundle); |
| } else if (waitTime <= 0) { |
| LOGGER.error("Kernel has failed to start before the timeout of {} seconds.", this.startupWaitTime); |
| kernelStartTimedOut(); |
| } else { |
| LOGGER.error("Bundle {} did not start within the Kernel timeout of {} seconds.", bundle, this.startupWaitTime); |
| kernelStartTimedOut(); |
| } |
| return; |
| } |
| } |
| } |
| } catch (FailureSignalledException fse) { |
| kernelStartFailed(fse.getCause()); |
| return; |
| } catch (Exception e) { |
| kernelStartFailed(e); |
| return; |
| } |
| |
| kernelStarted(); |
| } finally { |
| this.serviceReferenceTracker.ungetAll(); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private EventLogger getEventLoggerService() { |
| EventLogger eventLogger = null; |
| ServiceReference<EventLogger> eventLoggerServiceReference = (ServiceReference<EventLogger>) this.context.getServiceReference(EventLogger.class.getName()); |
| if (eventLoggerServiceReference != null) { |
| eventLogger = (EventLogger) this.context.getService(this.serviceReferenceTracker.track(eventLoggerServiceReference)); |
| } |
| return eventLogger; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private EventAdmin getEventAdminService() { |
| EventAdmin eventAdmin = null; |
| ServiceReference<EventAdmin> eventAdminServiceReference = (ServiceReference<EventAdmin>) this.context.getServiceReference(EventAdmin.class.getName()); |
| if (eventAdminServiceReference != null) { |
| eventAdmin = (EventAdmin) this.context.getService(this.serviceReferenceTracker.track(eventAdminServiceReference)); |
| } |
| return eventAdmin; |
| } |
| |
| private boolean isKernelBundle(Bundle bundle) { |
| String symbolicName = bundle.getSymbolicName(); |
| return symbolicName != null && symbolicName.startsWith(KERNEL_BSN_PREFIX); |
| } |
| |
| private void kernelStarting() { |
| postEvent(KERNEL_EVENT_STARTING); |
| logEvent(KernelLogEvents.KERNEL_STARTING); |
| } |
| |
| private void kernelStarted() { |
| this.kernelStatus.setStarted(); |
| postEvent(KERNEL_EVENT_STARTED); |
| logEvent(KernelLogEvents.KERNEL_STARTED); |
| } |
| |
| private void kernelStartAborted(Bundle bundle) { |
| postEvent(KERNEL_EVENT_START_ABORTED); |
| logEvent(KernelLogEvents.KERNEL_EVENT_START_ABORTED, bundle.getSymbolicName(), bundle.getVersion()); |
| generateDumpAndShutdown("startupTimedOut", null); |
| } |
| |
| private void kernelStartTimedOut() { |
| postEvent(KERNEL_EVENT_START_TIMED_OUT); |
| logEvent(KernelLogEvents.KERNEL_START_TIMED_OUT, this.startupWaitTime); |
| generateDumpAndShutdown("startupTimedOut", null); |
| } |
| |
| private void kernelStartFailed(Throwable failure) { |
| postEvent(KERNEL_EVENT_START_FAILED); |
| logEvent(KernelLogEvents.KERNEL_START_FAILED, failure); |
| generateDumpAndShutdown("startupFailed", failure); |
| } |
| |
| private void generateDumpAndShutdown(String cause, Throwable failure) { |
| if (failure != null) { |
| this.dumpGenerator.generateDump(cause, failure); |
| } else { |
| this.dumpGenerator.generateDump(cause); |
| } |
| this.shutdown.immediateShutdown(); |
| } |
| |
| private void logEvent(KernelLogEvents event, Throwable throwable, Object...args) { |
| if (this.eventLogger != null) { |
| this.eventLogger.log(event, throwable, args); |
| } |
| } |
| |
| private void logEvent(KernelLogEvents event, Object... args) { |
| this.logEvent(event, null, args); |
| } |
| |
| private void postEvent(String topic) { |
| if (this.eventAdmin != null) { |
| this.eventAdmin.postEvent(new Event(topic, (Map<String, ?>)null)); |
| } |
| } |
| } |
| } |