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

org.eclipse.osgi.internal.hooks.EclipseLazyStarter Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2006, 2020 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.osgi.internal.hooks;

import java.security.AccessController;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.osgi.container.Module;
import org.eclipse.osgi.container.Module.StartOptions;
import org.eclipse.osgi.container.Module.State;
import org.eclipse.osgi.container.ModuleCapability;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.framework.util.SecureAction;
import org.eclipse.osgi.internal.framework.EquinoxContainer;
import org.eclipse.osgi.internal.hookregistry.ClassLoaderHook;
import org.eclipse.osgi.internal.loader.classpath.ClasspathManager;
import org.eclipse.osgi.internal.messages.Msg;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkEvent;

public class EclipseLazyStarter extends ClassLoaderHook {
	private static final EnumSet alreadyActive = EnumSet.of(State.ACTIVE, State.STOPPING, State.UNINSTALLED);
	private static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());

	// holds the initiating class name
	private final ThreadLocal initiatingClassName = new ThreadLocal<>();
	// holds the ClasspathManagers that need to be activated
	private final ThreadLocal> activationStack = new ThreadLocal<>();
	// used to store exceptions that occurred while activating a bundle
	// keyed by ClasspathManager->Exception
	// WeakHashMap is used to prevent pinning the ClasspathManager objects.
	private final Map errors = Collections.synchronizedMap(new WeakHashMap());

	private final EquinoxContainer container;

	public EclipseLazyStarter(EquinoxContainer container) {
		this.container = container;
	}

	@Override
	public void preFindLocalClass(String name, ClasspathManager manager) throws ClassNotFoundException {
		if (initiatingClassName.get() == null) {
			initiatingClassName.set(name);
		}
		ModuleRevision revision = manager.getGeneration().getRevision();
		Module module = revision.getRevisions().getModule();
		// If the bundle is active, uninstalled or stopping then the bundle has already
		// been initialized (though it may have been destroyed) so just return the class.
		if (alreadyActive.contains(module.getState()))
			return;
		// The bundle is not active and does not require activation, just return the class
		if (!shouldActivateFor(name, module, revision, manager))
			return;
		Deque stack = activationStack.get();
		if (stack == null) {
			stack = new ArrayDeque<>(6);
			activationStack.set(stack);
		}
		// each element is a classpath manager that must be activated after
		// the initiating class has been defined (see postFindLocalClass)
		if (!stack.contains(manager)) {
			// only add the manager if it has not been added already
			stack.addFirst(manager);
		}
	}

	@Override
	public void postFindLocalClass(String name, Class clazz, ClasspathManager manager) throws ClassNotFoundException {
		if (initiatingClassName.get() != name)
			return;
		initiatingClassName.set(null);
		Deque stack = activationStack.get();
		if (stack == null || stack.isEmpty())
			return;

		// if we have a stack we must clear it even if (clazz == null)
		List managers = new ArrayList<>(stack);
		stack.clear();
		if (clazz == null)
			return;
		for (ClasspathManager managerElement : managers) {
			if (errors.get(managerElement) != null) {
				if (container.getConfiguration().throwErrorOnFailedStart)
					throw errors.get(managerElement);
				continue;
			}

			// The bundle must be started.
			// Note that another thread may already be starting this bundle;
			// In this case we will timeout after a default of 5 seconds and record the BundleException
			long startTime = System.currentTimeMillis();
			Module m = managerElement.getGeneration().getRevision().getRevisions().getModule();
			try {
				// do not persist the start of this bundle
				secureAction.start(m, StartOptions.LAZY_TRIGGER);
			} catch (BundleException e) {
				Bundle bundle = managerElement.getGeneration().getRevision().getBundle();
				if (e.getType() == BundleException.STATECHANGE_ERROR) {
					String message = NLS.bind(Msg.ECLIPSE_CLASSLOADER_CONCURRENT_STARTUP, new Object[] {Thread.currentThread(), name, m.getStateChangeOwner(), bundle, Long.valueOf(System.currentTimeMillis() - startTime)});
					container.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.WARNING, message, e);
					continue;
				}
				String message = NLS.bind(Msg.ECLIPSE_CLASSLOADER_ACTIVATION, bundle.getSymbolicName(), Long.toString(bundle.getBundleId()));
				ClassNotFoundException error = new ClassNotFoundException(message, e);
				errors.put(managerElement, error);
				if (container.getConfiguration().throwErrorOnFailedStart) {
					container.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, message, e, null);
					throw error;
				}
				container.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundle, new BundleException(message, e));
			}
		}
	}

	private boolean shouldActivateFor(String className, Module module, ModuleRevision revision, ClasspathManager manager) throws ClassNotFoundException {
		State state = module.getState();
		if (!State.LAZY_STARTING.equals(state)) {
			if (State.STARTING.equals(state) && manager.getClassLoader().getBundleLoader().isTriggerSet()) {
				// the trigger has been set but we are waiting for the activation to complete
				return true;
			}
			// Don't activate non-starting bundles
			if (State.RESOLVED.equals(module.getState())) {
				// handle the resolved case where a previous error occurred
				if (container.getConfiguration().throwErrorOnFailedStart) {
					ClassNotFoundException error = errors.get(manager);
					if (error != null)
						throw error;
				}
				// The module is persistently started and has the lazy activation policy but has not entered the LAZY_STARTING state
				// There are 2 cases where this can happen
				// 1) The start-level thread has not gotten to transitioning the bundle to LAZY_STARTING yet
				// 2) The bundle is marked for eager activation and the start-level thread has not activated it yet
				// In both cases we need to fire the lazy start trigger to activate the bundle if the start-level is met
				return module.isPersistentlyStarted() && isLazyStartable(className, revision);
			}
			return false;
		}

		return isLazyStartable(className, revision);
	}

	private boolean isLazyStartable(String className, ModuleRevision revision) {
		if (!revision.hasLazyActivatePolicy()) {
			return false;
		}
		List moduleDatas = revision.getModuleCapabilities(EquinoxModuleDataNamespace.MODULE_DATA_NAMESPACE);
		if (moduleDatas.isEmpty()) {
			return false;
		}

		Map moduleDataAttrs = moduleDatas.get(0).getAttributes();
		@SuppressWarnings("unchecked")
		List excludes = (List) moduleDataAttrs.get(EquinoxModuleDataNamespace.CAPABILITY_LAZY_EXCLUDE_ATTRIBUTE);
		@SuppressWarnings("unchecked")
		List includes = (List) moduleDataAttrs.get(EquinoxModuleDataNamespace.CAPABILITY_LAZY_INCLUDE_ATTRIBUTE);
		// no exceptions, it is easy to figure it out
		if (excludes == null && includes == null)
			return true;
		// otherwise, we need to check if the package is in the exceptions list
		int dotPosition = className.lastIndexOf('.');
		// the class has no package name... no exceptions apply
		if (dotPosition == -1)
			return true;
		String packageName = className.substring(0, dotPosition);
		return ((includes == null || includes.contains(packageName)) && (excludes == null || !excludes.contains(packageName)));
	}

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

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy