Hi,
In servicemix 4 we are implementing a command line console who's
commands can be plugged via OSGI bundles. One of the things we would
like to do is be able to take a arguments on the java command line and
execute that as a application command. But part of the problem is
that the bundle that gets those handles getting the java command line
args and calling a command implemented via another bundle usually
loads before all the command bundles get loaded. Yeah I know we could
use start levels to solve that start ordering problem. But I think
it's a bit of pain having to manually specify the start levels for
bundles.
So I started playing with the idea of letting the command bundles to
declare via a bundle header that it should get started before the
"argument parsing/command calling" bundle. I made a little patch to
felix that you might want to consider including. It's been
generalized a little to allow declaring that other bundles should get
started before or after etc.
The patch extends felix to use the "Felix-Start-Order" bundle manifest
header. The header is used to control the preferred start order as
seen by the bundle in relation to other bundles. If not specified
bundles are started in the order of the Bundle id (the current
behaviour). The header is set to a comma separated list specifying
the order the bundle would like to see things started in.
Examples:
To start a bundle last:
Felix-Start-Order: :all, :this
To start a bundle first:
Felix-Start-Order: :this, :all
To start a bundle before bundle foo and bar:
Felix-Start-Order: :this, foo, bar
To start a bundle after bundle foo and bar:
Felix-Start-Order: foo, bar, :this
To start a bundle after foo, but before bar:
Felix-Start-Order: foo, :this, bar
The current bundle is represented in the list by the ":this" token.
The ":all" token represents all other
bundles. Anything else should be a symbolic name of another bundle.
Anyways.. I hope someone gets time to review that patch and provide feedback.
--
Regards,
Hiram
Blog: http://hiramchirino.com
Open Source SOA
http://open.iona.com
Index: framework/src/main/java/org/apache/felix/framework/Felix.java
===================================================================
--- framework/src/main/java/org/apache/felix/framework/Felix.java (revision 602106)
+++ framework/src/main/java/org/apache/felix/framework/Felix.java (working copy)
@@ -1008,55 +1008,56 @@
// lowered or raised to that the bundles can be efficiently
// processed in order. Within a start level sort by bundle ID.
Comparator comparator = null;
- if (lowering)
- {
- // Sort descending to stop highest start level first.
- comparator = new Comparator() {
- public int compare(Object o1, Object o2)
+
+ // Sort ascending to start lowest start level first.
+ comparator = new Comparator() {
+ public int compare(Object o1, Object o2)
+ {
+ FelixBundle b1 = (FelixBundle) o1;
+ FelixBundle b2 = (FelixBundle) o2;
+ if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
+ > b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
{
- FelixBundle b1 = (FelixBundle) o1;
- FelixBundle b2 = (FelixBundle) o2;
- if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
- < b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
- {
- return 1;
- }
- else if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
- > b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
- {
- return -1;
- }
- else if (b1.getInfo().getBundleId() < b2.getInfo().getBundleId())
- {
- return 1;
- }
+ return 1;
+ }
+ else if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
+ < b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
+ {
return -1;
}
- };
- }
- else
+
+ // See if the bundles have a preference on the start order by configuring a
+ // Felix-Start-Order manifest header.
+ String name = (String) b1.getInfo().getCurrentHeader().get(Constants.BUNDLE_SYMBOLICNAME);
+ String order = (String) b1.getInfo().getCurrentHeader().get(FelixConstants.FELIX_START_ORDER);
+ FelixBundleStartOrder b1Order = new FelixBundleStartOrder(name, order);
+
+ name = (String) b2.getInfo().getCurrentHeader().get(Constants.BUNDLE_SYMBOLICNAME);
+ order = (String) b2.getInfo().getCurrentHeader().get(FelixConstants.FELIX_START_ORDER);
+ FelixBundleStartOrder b2Order = new FelixBundleStartOrder(name, order);
+
+ int rc = FelixBundleStartOrder.compare(b1Order, b2Order);
+ if( rc!=0 ) {
+ return rc;
+ }
+
+ else if (b1.getInfo().getBundleId() > b2.getInfo().getBundleId())
+ {
+ return 1;
+ }
+ return -1;
+ }
+ };
+
+ if (lowering)
{
- // Sort ascending to start lowest start level first.
+ final Comparator assendingComparator = comparator;
+ // Sort descending to stop highest start level first.
+ // The decending Comparator is just the inverse of the assending Comparator
comparator = new Comparator() {
public int compare(Object o1, Object o2)
{
- FelixBundle b1 = (FelixBundle) o1;
- FelixBundle b2 = (FelixBundle) o2;
- if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
- > b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
- {
- return 1;
- }
- else if (b1.getInfo().getStartLevel(getInitialBundleStartLevel())
- < b2.getInfo().getStartLevel(getInitialBundleStartLevel()))
- {
- return -1;
- }
- else if (b1.getInfo().getBundleId() > b2.getInfo().getBundleId())
- {
- return 1;
- }
- return -1;
+ return assendingComparator.compare(o1, o2) * -1;
}
};
}
Index: framework/src/main/java/org/apache/felix/framework/FelixBundleStartOrder.java
===================================================================
--- framework/src/main/java/org/apache/felix/framework/FelixBundleStartOrder.java (revision 0)
+++ framework/src/main/java/org/apache/felix/framework/FelixBundleStartOrder.java (revision 0)
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.framework;
+
+import java.util.HashSet;
+
+/**
+ * Used to process the "Felix-Start-Order" bundle manifest header. The header is used to control
+ * the preferred start order as seen by the bundle in relation to other bundles. If not specified
+ * bundles are started in the order of the Bundle id.
+ *
+ * The syntax of the header looks like:
+ *
+ * Felix-Start-Order: ((<bundle-symbolic-name>|:all),)* :this (,(<bundle-symbolic-name>|:all))*
+ *
+ * It is a comma separated list specifying the order the bundle would like to see things started in.
+ * The bundle is represented in the list by the ":this" token. The ":all" token represents all other
+ * bundles. The <bundle-symbolic-name> should be the symbolic name of another bundle.
+ *
+ * Examples:
+ *
+ * To start a bundle last:
+ * Felix-Start-Order: :all, :this
+ *
+ * To start a bundle first:
+ * Felix-Start-Order: :this, :all
+ *
+ * To start a bundle before bundle foo and bar:
+ * Felix-Start-Order: :this, foo, abr
+ *
+ * To start a bundle after bundle foo and bar:
+ * Felix-Start-Order: foo, bar, :this
+ *
+ * To start a bundle after foo, but before bar:
+ * Felix-Start-Order: foo, :this, bar
+ *
+ * The ":this" token must appear in the list . The ":all" token should only appear
+ * either before or after the ":this" token. Should these conditions not hold, the
+ * start order is considered invalid and the "Felix-Start-Order" header is ignored.
+ *
+ * @author chirino
+ */
+public final class FelixBundleStartOrder {
+
+ // The bundle names that came before this one.
+ private final HashSet before = new HashSet();
+ // The bundle names that came after this one.
+ private final HashSet after = new HashSet();
+ // The name of this bundle.
+ private final String name;
+ // Was the :all wild card after this bundle?
+ private boolean allAfter;
+ // Was the :all wild card before this bundle?
+ private boolean allBefore;
+
+ /**
+ * @param name - The symbolic name of the bundle
+ * @param order - The order configuration string of the bundle, can be null.
+ */
+ public FelixBundleStartOrder(String name, String order) {
+ this.name = name;
+ if( order!=null ) {
+
+ String[] split = order.split(",");
+ boolean onAfter = false;
+ for (int i = 0; i < split.length; i++) {
+ String bundle = split[i].trim();
+ if( bundle.length()>0 ) {
+ if( ":this".equals(bundle)) {
+ onAfter = true;
+ }
+ if( onAfter ) {
+ if( ":all".equals(bundle)) {
+ allAfter = true;
+ }
+ after.add(bundle);
+ } else {
+ allBefore = true;
+ before.add(bundle);
+ }
+ }
+ }
+
+ // Do some validation of the order provided.
+ // The :this marker must be specified.
+ // The :all marker must be only specified either before or after :this
+ // Explict bundles cannot be listed before AND after.
+ HashSet intersection = new HashSet(before);
+ intersection.retainAll(after);
+ if ( !onAfter || (allAfter && allBefore) || !intersection.isEmpty()) {
+ // Reset the data so we behave like if the FELIX_START_ORDER
+ // was not set.
+ before.clear();
+ after.clear();
+ allBefore=false;
+ allAfter=false;
+ }
+ }
+ }
+
+ /**
+ * Compares two FelixBundleStartOrder objects.
+ * @return 1 if o1 comes after o2, -1 if o1 comes before o2
+ */
+ public static int compare(FelixBundleStartOrder o1, FelixBundleStartOrder o2) {
+
+ // if o1 configured "o2, :this"
+ if( o1.before.contains(o2.name) ) {
+ // if o2 configured "o1, :this"
+ if( o2.before.contains(o1.name) ) {
+ // We might want to warn that the 2 modules listed each
+ // other as being before each other.
+ return 0;
+ }
+ return 1;
+ }
+
+ // if o2 configured "o1, :this"
+ if( o2.before.contains(o1.name) ) {
+ return -1;
+ }
+
+ // if o1 configured ":this, o2"
+ if( o1.after.contains(o2.name) ) {
+ // if o2 configured ":this, o1"
+ if( o2.after.contains(o1.name) ) {
+ // We might want to warn that the 2 modules listed each
+ // other as being after each other.
+ return 0;
+ }
+ return -1;
+ }
+
+ // if o2 configured ":this, o1"
+ if( o2.after.contains(o1.name) ) {
+ return 1;
+ }
+
+ // If we got here then the modules did not Explicitly list each other
+ // in the order statement. Let see if one of them is using the :all
+ // wild card.
+
+ // if o1 configured ":all, :this"
+ if( o1.allBefore ) {
+ // if o2 configured ":all, :this"
+ if( o2.allBefore ) {
+ return 0;
+ }
+ return 1;
+ }
+ // if o2 configured ":all, :this"
+ if( o2.allBefore ) {
+ return -1;
+ }
+
+ // if o1 configured ":this, :all"
+ if( o1.allAfter ) {
+ // if o2 configured ":this, :all"
+ if( o2.allAfter ) {
+ return 0;
+ }
+ return -1;
+ }
+
+ // if o2 configured ":this, :all"
+ if( o2.allAfter ) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+}
\ No newline at end of file
Property changes on: framework/src/main/java/org/apache/felix/framework/FelixBundleStartOrder.java
___________________________________________________________________
Name: svn:keywords
+ Rev Date
Name: svn:eol-style
+ native
Index: framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
===================================================================
--- framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java (revision 602106)
+++ framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java (working copy)
@@ -62,4 +62,5 @@
// Miscellaneous properties values.
public static final String FAKE_URL_PROTOCOL_VALUE = "location:";
public static final String FELIX_EXTENSION_ACTIVATOR = "Felix-Activator";
+ public static final String FELIX_START_ORDER = "Felix-Start-Order";
}
\ No newline at end of file