You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2017/11/09 10:21:11 UTC
svn commit: r1814713 - in /felix/trunk/scr: ./
src/main/java/org/apache/felix/scr/impl/
Author: cziegeler
Date: Thu Nov 9 10:21:11 2017
New Revision: 1814713
URL: http://svn.apache.org/viewvc?rev=1814713&view=rev
Log:
FELIX-5739 : Strange behaviour with Lazy-ActivationPolicy and autostart
Added:
felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/AbstractExtender.java (with props)
Modified:
felix/trunk/scr/bnd.bnd
felix/trunk/scr/pom.xml
felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java
felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java
Modified: felix/trunk/scr/bnd.bnd
URL: http://svn.apache.org/viewvc/felix/trunk/scr/bnd.bnd?rev=1814713&r1=1814712&r2=1814713&view=diff
==============================================================================
--- felix/trunk/scr/bnd.bnd (original)
+++ felix/trunk/scr/bnd.bnd Thu Nov 9 10:21:11 2017
@@ -21,8 +21,7 @@ Export-Package: org.apache.felix.scr.com
org.osgi.util.function;version=1.0, \
org.osgi.util.promise;version=1.0
-Private-Package: org.apache.felix.scr.impl.*, \
- org.apache.felix.utils.extender
+Private-Package: org.apache.felix.scr.impl.*
# Configuration Admin is optional and dynamic, but allow eager wiring by importing it
# LogService is optional but if present the R4.0 version 1.3 is sufficient.
Modified: felix/trunk/scr/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/scr/pom.xml?rev=1814713&r1=1814712&r2=1814713&view=diff
==============================================================================
--- felix/trunk/scr/pom.xml (original)
+++ felix/trunk/scr/pom.xml Thu Nov 9 10:21:11 2017
@@ -102,6 +102,18 @@
</dependency>
<dependency>
<groupId>org.osgi</groupId>
+ <artifactId>org.osgi.service.log</artifactId>
+ <version>1.3.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.service.metatype</artifactId>
+ <version>1.3.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
<artifactId>org.osgi.namespace.extender</artifactId>
<version>1.0.1</version>
<scope>provided</scope>
@@ -113,12 +125,6 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.utils</artifactId>
- <version>1.8.6</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
<groupId>${project.groupId}</groupId>
<artifactId>org.apache.felix.shell</artifactId>
<version>1.0.0</version>
Added: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/AbstractExtender.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/AbstractExtender.java?rev=1814713&view=auto
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/AbstractExtender.java (added)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/AbstractExtender.java Thu Nov 9 10:21:11 2017
@@ -0,0 +1,257 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.scr.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+
+/**
+ * Base class to write bundle extenders.
+ * This extender tracks started bundles (or starting if they have a lazy activation
+ * policy) and will create an extension for each of them to manage it.
+ *
+ * The extender will handle all concurrency and synchronization issues.
+ *
+ * The extender guarantee that all extensions will be stopped synchronously with
+ * the STOPPING event of a given bundle and that all extensions will be stopped
+ * before the extender bundle is stopped.
+ *
+ */
+public abstract class AbstractExtender implements BundleActivator, BundleTrackerCustomizer<Bundle>, SynchronousBundleListener {
+
+ private final ConcurrentMap<Bundle, Activator.ScrExtension> extensions = new ConcurrentHashMap<Bundle, Activator.ScrExtension>();
+ private final ConcurrentMap<Bundle, FutureTask<Void>> destroying = new ConcurrentHashMap<Bundle, FutureTask<Void>>();
+ private volatile boolean stopping;
+ private volatile boolean stopped;
+
+ private BundleContext context;
+ private BundleTracker<Bundle> tracker;
+
+ public BundleContext getBundleContext() {
+ return context;
+ }
+
+ public boolean isStopping() {
+ return stopping;
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ this.context = context;
+ this.context.addBundleListener(this);
+ this.tracker = new BundleTracker<Bundle>(this.context, Bundle.ACTIVE | Bundle.STARTING, this);
+ doStart();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ stopping = true;
+ while (!extensions.isEmpty()) {
+ Collection<Bundle> toDestroy = chooseBundlesToDestroy(extensions.keySet());
+ if (toDestroy == null || toDestroy.isEmpty()) {
+ toDestroy = new ArrayList<Bundle>(extensions.keySet());
+ }
+ for (Bundle bundle : toDestroy) {
+ destroyExtension(bundle);
+ }
+ }
+ doStop();
+ stopped = true;
+ }
+
+ protected void doStart() throws Exception {
+ startTracking();
+ }
+
+ protected void doStop() throws Exception {
+ stopTracking();
+ }
+
+ protected void startTracking() {
+ this.tracker.open();
+ }
+
+ protected void stopTracking() {
+ this.tracker.close();
+ }
+
+ /**
+ * Create the executor used to start extensions asynchronously.
+ *
+ * @return an
+ */
+ protected ExecutorService createExecutor() {
+ return Executors.newScheduledThreadPool(3);
+ }
+
+ protected Collection<Bundle> chooseBundlesToDestroy(Set<Bundle> bundles) {
+ return null;
+ }
+
+
+ @Override
+ public void bundleChanged(BundleEvent event) {
+ if (stopped) {
+ return;
+ }
+ Bundle bundle = event.getBundle();
+ if (bundle.getState() != Bundle.ACTIVE && bundle.getState() != Bundle.STARTING) {
+ // The bundle is not in STARTING or ACTIVE state anymore
+ // so destroy the context. Ignore our own bundle since it
+ // needs to kick the orderly shutdown.
+ if (bundle != this.context.getBundle()) {
+ destroyExtension(bundle);
+ }
+ }
+ }
+
+ @Override
+ public Bundle addingBundle(Bundle bundle, BundleEvent event) {
+ modifiedBundle(bundle, event, bundle);
+ return bundle;
+ }
+
+ @Override
+ public void modifiedBundle(Bundle bundle, BundleEvent event, Bundle object) {
+ if (bundle.getState() != Bundle.ACTIVE && bundle.getState() != Bundle.STARTING) {
+ // The bundle is not in STARTING or ACTIVE state anymore
+ // so destroy the context. Ignore our own bundle since it
+ // needs to kick the orderly shutdown and not unregister the namespaces.
+ if (bundle != this.context.getBundle()) {
+ destroyExtension(bundle);
+ }
+ return;
+ }
+ // Do not track bundles given we are stopping
+ if (stopping) {
+ return;
+ }
+ // For starting bundles, ensure, it's a lazy activation,
+ // else we'll wait for the bundle to become ACTIVE
+ if (bundle.getState() == Bundle.STARTING) {
+ String activationPolicyHeader = bundle.getHeaders("").get(Constants.BUNDLE_ACTIVATIONPOLICY);
+ if (activationPolicyHeader == null
+ || !activationPolicyHeader.startsWith(Constants.ACTIVATION_LAZY)
+ || !bundle.adapt(BundleStartLevel.class).isActivationPolicyUsed()) {
+ // Do not track this bundle yet
+ return;
+ }
+ }
+ createExtension(bundle);
+ }
+
+ @Override
+ public void removedBundle(Bundle bundle, BundleEvent event, Bundle object) {
+ // Nothing to do
+ destroyExtension(bundle);
+ }
+
+ private void createExtension(final Bundle bundle) {
+ try {
+ BundleContext bundleContext = bundle.getBundleContext();
+ if (bundleContext == null) {
+ // The bundle has been stopped in the mean time
+ return;
+ }
+ final Activator.ScrExtension extension = doCreateExtension(bundle);
+ if (extension == null) {
+ // This bundle is not to be extended
+ return;
+ }
+ synchronized (extensions) {
+ if (extensions.putIfAbsent(bundle, extension) != null) {
+ return;
+ }
+ }
+ debug(bundle, "Starting extension synchronously");
+ extension.start();
+ } catch (Throwable t) {
+ warn(bundle, "Error while creating extension", t);
+ }
+ }
+
+ private void destroyExtension(final Bundle bundle) {
+ FutureTask<Void> future;
+ synchronized (extensions) {
+ debug(bundle, "Starting destruction process");
+ future = destroying.get(bundle);
+ if (future == null) {
+ final Activator.ScrExtension extension = extensions.remove(bundle);
+ if (extension != null) {
+ debug(bundle, "Scheduling extension destruction");
+ future = new FutureTask<Void>(new Runnable() {
+ @Override
+ public void run() {
+ debug(bundle, "Destroying extension");
+ try {
+ extension.destroy();
+ } catch (Exception e) {
+ warn(bundle, "Error while destroying extension", e);
+ } finally {
+ debug(bundle, "Finished destroying extension");
+ synchronized (extensions) {
+ destroying.remove(bundle);
+ }
+ }
+ }
+ }, null);
+ destroying.put(bundle, future);
+ } else {
+ debug(bundle, "Not an extended bundle or destruction of extension already finished");
+ }
+ } else {
+ debug(bundle, "Destruction already scheduled");
+ }
+ }
+ if (future != null) {
+ try {
+ debug(bundle, "Waiting for extension destruction");
+ future.run();
+ future.get();
+ } catch (Throwable t) {
+ warn(bundle, "Error while destroying extension", t);
+ }
+ }
+ }
+
+ /**
+ * Create the extension for the given bundle, or null if the bundle is not to be extended.
+ *
+ * @param bundle the bundle to extend
+ * @return The extension
+ * @throws Exception If something goes wrong
+ */
+ protected abstract Activator.ScrExtension doCreateExtension(Bundle bundle) throws Exception;
+
+ protected abstract void debug(Bundle bundle, String msg);
+ protected abstract void warn(Bundle bundle, String msg, Throwable t);
+}
Propchange: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/AbstractExtender.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/AbstractExtender.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java?rev=1814713&r1=1814712&r2=1814713&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/Activator.java Thu Nov 9 10:21:11 2017
@@ -31,8 +31,6 @@ import org.apache.felix.scr.impl.config.
import org.apache.felix.scr.impl.helper.SimpleLogger;
import org.apache.felix.scr.impl.inject.ClassUtils;
import org.apache.felix.scr.impl.runtime.ServiceComponentRuntimeImpl;
-import org.apache.felix.utils.extender.AbstractExtender;
-import org.apache.felix.utils.extender.Extension;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@@ -86,7 +84,6 @@ public class Activator extends AbstractE
public Activator()
{
m_configuration = new ScrConfigurationImpl( this );
- setSynchronous( true );
}
/**
@@ -229,12 +226,12 @@ public class Activator extends AbstractE
//---------- Component Management -----------------------------------------
@Override
- protected Extension doCreateExtension(final Bundle bundle) throws Exception
+ protected Activator.ScrExtension doCreateExtension(final Bundle bundle) throws Exception
{
return new ScrExtension( bundle );
}
- protected class ScrExtension implements Extension
+ protected class ScrExtension
{
private final Bundle bundle;
@@ -463,18 +460,12 @@ public class Activator extends AbstractE
}
@Override
- protected void error(String msg, Throwable t)
- {
- log( LogService.LOG_DEBUG, m_bundle, msg, t );
- }
-
- // @Override
public void log(int level, String message, Throwable ex)
{
log( level, null, message, ex );
}
- // @Override
+ @Override
public void log(int level, String pattern, Object[] arguments, Throwable ex)
{
if ( isLogEnabled( level ) )
@@ -496,6 +487,7 @@ public class Activator extends AbstractE
/**
* Returns <code>true</code> if logging for the given level is enabled.
*/
+ @Override
public boolean isLogEnabled(int level)
{
return m_configuration == null || m_configuration.getLogLevel() >= level;
Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java?rev=1814713&r1=1814712&r2=1814713&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ComponentRegistry.java Thu Nov 9 10:21:11 2017
@@ -41,7 +41,6 @@ import org.apache.felix.scr.impl.manager
import org.apache.felix.scr.impl.metadata.ComponentMetadata;
import org.apache.felix.scr.impl.metadata.TargetedPID;
import org.osgi.framework.Bundle;
-import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentConstants;
@@ -444,48 +443,6 @@ public class ComponentRegistry
//---------- Helper method
- /**
- * Returns <code>true</code> if the <code>bundle</code> is to be considered
- * active from the perspective of declarative services.
- * <p>
- * As of R4.1 a bundle may have lazy activation policy which means a bundle
- * remains in the STARTING state until a class is loaded from that bundle
- * (unless that class is declared to not cause the bundle to start). And
- * thus for DS 1.1 this means components are to be loaded for lazily started
- * bundles being in the STARTING state (after the LAZY_ACTIVATION event) has
- * been sent. Hence DS must consider a bundle active when it is really
- * active and when it is a lazily activated bundle in the STARTING state.
- *
- * @param bundle The bundle check
- * @return <code>true</code> if <code>bundle</code> is not <code>null</code>
- * and the bundle is either active or has lazy activation policy
- * and is in the starting state.
- *
- * @see <a href="https://issues.apache.org/jira/browse/FELIX-1666">FELIX-1666</a>
- */
- static boolean isBundleActive( final Bundle bundle )
- {
- if ( bundle != null )
- {
- if ( bundle.getState() == Bundle.ACTIVE )
- {
- return true;
- }
-
- if ( bundle.getState() == Bundle.STARTING )
- {
- // according to the spec the activationPolicy header is only
- // set to request a bundle to be lazily activated. So in this
- // simple check we just verify the header is set to assume
- // the bundle is considered a lazily activated bundle
- return bundle.getHeaders("").get(Constants.BUNDLE_ACTIVATIONPOLICY) != null;
- }
- }
-
- // fall back: bundle is not considered active
- return false;
- }
-
private final ThreadLocal<List<ServiceReference<?>>> circularInfos = new ThreadLocal<List<ServiceReference<?>>> ()
{
@@ -588,6 +545,7 @@ public class ComponentRegistry
Runnable runnable = new Runnable()
{
+ @Override
@SuppressWarnings("unchecked")
public void run()
{
Modified: felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java
URL: http://svn.apache.org/viewvc/felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java?rev=1814713&r1=1814712&r2=1814713&view=diff
==============================================================================
--- felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java (original)
+++ felix/trunk/scr/src/main/java/org/apache/felix/scr/impl/ScrCommand.java Thu Nov 9 10:21:11 2017
@@ -58,6 +58,7 @@ public class ScrCommand implements ScrIn
private static final Comparator<ComponentDescriptionDTO> DESCRIPTION_COMP = new Comparator<ComponentDescriptionDTO>()
{
+ @Override
public int compare(final ComponentDescriptionDTO c1, final ComponentDescriptionDTO c2)
{
final long bundleId1 = c1.bundle.id;
@@ -85,6 +86,7 @@ public class ScrCommand implements ScrIn
private static final Comparator<ComponentConfigurationDTO> CONFIGURATION_COMP = new Comparator<ComponentConfigurationDTO>()
{
+ @Override
public int compare(final ComponentConfigurationDTO c1, final ComponentConfigurationDTO c2)
{
return Long.signum(c1.id - c2.id);
@@ -195,9 +197,52 @@ public class ScrCommand implements ScrIn
}
}
+ /**
+ * Returns <code>true</code> if the <code>bundle</code> is to be considered
+ * active from the perspective of declarative services.
+ * <p>
+ * As of R4.1 a bundle may have lazy activation policy which means a bundle
+ * remains in the STARTING state until a class is loaded from that bundle
+ * (unless that class is declared to not cause the bundle to start). And
+ * thus for DS 1.1 this means components are to be loaded for lazily started
+ * bundles being in the STARTING state (after the LAZY_ACTIVATION event) has
+ * been sent. Hence DS must consider a bundle active when it is really
+ * active and when it is a lazily activated bundle in the STARTING state.
+ *
+ * @param bundle The bundle check
+ * @return <code>true</code> if <code>bundle</code> is not <code>null</code>
+ * and the bundle is either active or has lazy activation policy
+ * and is in the starting state.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/FELIX-1666">FELIX-1666</a>
+ */
+ private static boolean isBundleActive( final Bundle bundle )
+ {
+ if ( bundle != null )
+ {
+ if ( bundle.getState() == Bundle.ACTIVE )
+ {
+ return true;
+ }
+
+ if ( bundle.getState() == Bundle.STARTING )
+ {
+ // according to the spec the activationPolicy header is only
+ // set to request a bundle to be lazily activated. So in this
+ // simple check we just verify the header is set to assume
+ // the bundle is considered a lazily activated bundle
+ return bundle.getHeaders("").get(Constants.BUNDLE_ACTIVATIONPOLICY) != null;
+ }
+ }
+
+ // fall back: bundle is not considered active
+ return false;
+ }
+
/* (non-Javadoc)
* @see org.apache.felix.scr.impl.ScrInfo#list(java.lang.String, java.io.PrintStream, java.io.PrintStream)
*/
+ @Override
public void list(final String bundleIdentifier, final PrintWriter out)
{
final List<ComponentDescriptionDTO> descriptions = new ArrayList<ComponentDescriptionDTO>();
@@ -228,7 +273,7 @@ public class ScrCommand implements ScrIn
{
throw new IllegalArgumentException("Missing bundle with ID " + bundleIdentifier);
}
- if (ComponentRegistry.isBundleActive(bundle))
+ if (isBundleActive(bundle))
{
descriptions.addAll(scrService.getComponentDescriptionDTOs(bundle));
if (descriptions.isEmpty())
@@ -287,6 +332,7 @@ public class ScrCommand implements ScrIn
/**
* @see org.apache.felix.scr.impl.ScrInfo#info(java.lang.String, java.io.PrintStream, java.io.PrintStream)
*/
+ @Override
public void info(final String componentId, final PrintWriter out)
{
final Result result = getComponentsFromArg(componentId, false);
@@ -402,7 +448,7 @@ public class ScrCommand implements ScrIn
{
out.println(" (No Component Configurations)");
}
- else
+ else
{
for (final ComponentConfigurationDTO cc: componentConfigurationDTOs)
{
@@ -546,6 +592,7 @@ public class ScrCommand implements ScrIn
/**
* @see org.apache.felix.scr.impl.ScrInfo#config(java.io.PrintStream)
*/
+ @Override
public void config(final PrintWriter out)
{
out.print("Log Level: ");