complete new implementation with support for service properties and detection of circular dependencies
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1406753 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole-plugins/servicediagnostics/README.txt b/webconsole-plugins/servicediagnostics/README.txt
index 7a6d23b..59a214b 100644
--- a/webconsole-plugins/servicediagnostics/README.txt
+++ b/webconsole-plugins/servicediagnostics/README.txt
@@ -1,7 +1,7 @@
OSGi Service Diagnostics and WebConsole Plugin
==============================================
-This projects aims at easing diagnostics of OSGi services and finding about missing dependencies.
+This projects aims at easing diagnostics of OSGi services and finding about missing and/or circular dependencies.
Typically in a large system with many cascading dependencies managed by different trackers such as DeclarativeService, DependencyManager or others, tracking the root cause of a top level service not being started can become very cumbersome. When building service oriented architectures, it is often the case that a single missing requirement will lock a full stack of services, but to find that one requirement is like finding a needle in a haystack!
@@ -48,5 +48,4 @@
Issues & TODOs:
===============
* use of JSONObject in Servlet.scala is a bit awkward, but Scala's native json is still incomplete..
-* no support for service properties; needs to be added.
* no support for iPojo, Blueprint, basic ServiceTrackers... more plugins could be developed. I only wrote the ones i'm using.
diff --git a/webconsole-plugins/servicediagnostics/core/pom.xml b/webconsole-plugins/servicediagnostics/core/pom.xml
index ead80eb..6760cce 100644
--- a/webconsole-plugins/servicediagnostics/core/pom.xml
+++ b/webconsole-plugins/servicediagnostics/core/pom.xml
@@ -27,7 +27,7 @@
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.webconsole</artifactId>
- <version>3.1.8</version>
+ <version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
@@ -71,11 +71,8 @@
org.apache.felix.servicediagnostics.webconsole
</Private-Package>
<Include-Resource>
- {maven-resources}, json-20090211.jar, scala-library-2.9.1.jar
+ {maven-resources}
</Include-Resource>
- <Bundle-ClassPath>
- ., json-20090211.jar, scala-library-2.9.1.jar
- </Bundle-ClassPath>
</instructions>
</configuration>
</plugin>
diff --git a/webconsole-plugins/servicediagnostics/core/src/main/resources/html/index.html b/webconsole-plugins/servicediagnostics/core/src/main/resources/html/index.html
index 9581ebf..85a2f84 100644
--- a/webconsole-plugins/servicediagnostics/core/src/main/resources/html/index.html
+++ b/webconsole-plugins/servicediagnostics/core/src/main/resources/html/index.html
@@ -41,11 +41,12 @@
var g = new Graph();
var empty = true;
- for (s in json) {
+ notavail = json.notavail;
+ for (s in notavail) {
empty = false;
- for (i = 0; i < json[s].length; i++) {
+ for (i = 0; i < notavail[s].length; i++) {
// point unregistered service to dependency name
- var dep = json[s][i];
+ var dep = notavail[s][i];
g.addNode(dep, {
getShape : function(r,x,y) {
// create a dashed square shape to differentiate the missing dependency
@@ -60,6 +61,14 @@
g.addEdge(s, dep, { directed : true } );
}
}
+ // show unresolved
+ var unresolved = json.unresolved
+ if (unresolved) for (s in unresolved) {
+ $("#warning").html("unresolvable components detected!");
+ for (i = 0; i < unresolved[s].length; i++) {
+ g.addEdge(s, unresolved[s][i], { directed : true } );
+ }
+ }
if (empty) {
$("#canvas").empty().append($("<h1>").html("Service Registry status OK: No unresolved service found."));
@@ -152,17 +161,22 @@
/* only do all this when document has finished loading (needed for RaphaelJS) */
$(document).ready(function(){
$("#actions")
- .append($("<a>").attr("href", "javascript:loadAllServices()").html("All"))
+ .append($("<a>").attr("href", "javascript:loadAllServices()").html("Show Service Registry"))
.append($("<span>").html(" | "))
- .append($("<a>").attr("href", "javascript:loadUnavail()").html("Unavail"))
+ .append($("<a>").attr("href", "javascript:loadUnavail()").html("Show Not Avail"))
});
-->
</script>
+ <style>
+ #actions a { color:black; font-weight:bold; text-decoration:none; }
+ #warning { color:red; font-weight:bold; }
+ </style>
</header>
<body>
Filter: <input type="text" id="filter"/><button id="redraw" onclick="redraw();">redraw</button>
<span id="actions"></span>
+ <span id="warning"></span>
<div id="canvas"></div>
</body>
</html>
diff --git a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/ServiceDiagnostics.scala b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/ServiceDiagnostics.scala
index 406c5c0..71a320a 100644
--- a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/ServiceDiagnostics.scala
+++ b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/ServiceDiagnostics.scala
@@ -28,10 +28,16 @@
/**
* returns a map of component name to list of leaf unresolved dependencies.
*/
- def notavail:Map[String, List[Dependency]]
+ def notavail:Map[String, List[String]]
+
+ /**
+ * returns a graph of unresolvable components (typically loops)
+ */
+ def unresolved :Map[String, List[String]]
/**
* returns a map of resolved service names to list of bundles using the service
*/
def allServices:Map[String, List[String]]
}
+
diff --git a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/ServiceDiagnosticsPlugin.scala b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/ServiceDiagnosticsPlugin.scala
index 41b2444..4fb847a 100644
--- a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/ServiceDiagnosticsPlugin.scala
+++ b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/ServiceDiagnosticsPlugin.scala
@@ -18,29 +18,67 @@
*/
package org.apache.felix.servicediagnostics
+import org.osgi.framework.FrameworkUtil
+import collection.JavaConversions._
+
+import Util._
+
/**
* This is the interface to be implemented by participating service injection frameworks
* such as SCR or DependencyManager.
- * Each plugin implementation is responsible for returning its own set of leaf unresolved dependencies.
+ * Each plugin implementation is responsible for returning its own set of components.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
trait ServiceDiagnosticsPlugin
{
- /**
- * returns a map of unresolved service name -> list of missing deps
- * leafs only: should not include intermediates
- * @return the unresolved service names
+ /**
+ * returns a list of all known components for this plugin
*/
- def getUnresolvedDependencies:Map[String, List[Dependency]]
+ def components:List[Comp]
+}
+
+/**
+ * This class represents a service component.
+ * @param name the service interface name
+ * (use different instances for objects registering multiple services)
+ * @param props the service properties
+ * @param registered true if the component is already registered in the Service Registry
+ * @param deps the list of declared dependencies
+ */
+class Comp(val name:String, val props:java.util.Dictionary[_,_], val registered:Boolean, val deps:List[Dependency])
+{
+ override def toString = {if (registered) "[registered]" else "[unregistered]"}+shorten(name)+{
+ if (props != null && !props.isEmpty) " "+props else ""}
}
/**
* This class represents a service dependency.
- * @param name the service name
+ * @param name the service interface name
* @param filter the optional service filter
+ * @param available true if the dependency is already available in the Service Registry
*/
-class Dependency(val name:String, val filter:String)
+class Dependency(val name:String, val filter:String, val available:Boolean = false)
{
- override def toString = name//+"("+filter+")" FIXME
+ private val compiled = if (filter != null && !filter.isEmpty) FrameworkUtil.createFilter(filter) else null
+
+ def matchedBy(comp:Comp):Boolean = comp.name == name &&
+ !(compiled != null && comp.props == null) && //filter and no props, doesn't match
+ (compiled == null || compiled.`match`(comp.props))
+
+ override def toString = shorten(name)+{if (filter != null) filter else ""}
+}
+
+/**
+ * utility methods
+ */
+object Util
+{
+ /**
+ * shorten "org.apache.felix.servicediagnostics.ServiceDiagnostics" to "o.a.f.s.ServiceDiagnostics"
+ */
+ def shorten(classname:String) :String = {
+ val l = classname.split('.').toList
+ l.map(_.take(1)).mkString(".") + l.last.drop(1)
+ }
}
diff --git a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/Activator.scala b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/Activator.scala
index 786d93b..e1ac15a 100644
--- a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/Activator.scala
+++ b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/Activator.scala
@@ -18,8 +18,7 @@
*/
package org.apache.felix.servicediagnostics.impl
-import scala.collection.mutable.Map
-import scala.collection.JavaConversions._
+import java.util. { Hashtable => jHT }
import javax.servlet.http.HttpServlet
@@ -78,7 +77,9 @@
// and the webconsole plugin itself
dm.add(createComponent
- .setInterface(classOf[javax.servlet.Servlet].getName, Map("felix.webconsole.label" -> "servicegraph"))
+ .setInterface(classOf[javax.servlet.Servlet].getName, new jHT[String,String]() {{
+ put("felix.webconsole.label", "servicegraph")
+ }})
.setImplementation(classOf[WebConsolePlugin]))
}
diff --git a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/DMNotAvail.scala b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/DMNotAvail.scala
index cccb04a..282e752 100644
--- a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/DMNotAvail.scala
+++ b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/DMNotAvail.scala
@@ -25,6 +25,7 @@
import org.osgi.framework.ServiceReference
import org.apache.felix.dm.DependencyManager
+import org.apache.felix.dm.Component
import org.apache.felix.dm.ComponentDeclaration
import org.apache.felix.dm.ComponentDeclaration._
import org.apache.felix.dm.ComponentDependencyDeclaration
@@ -39,42 +40,23 @@
*/
class DMNotAvail(val bc:BundleContext) extends ServiceDiagnosticsPlugin
{
-
- /**
- * implements ServiceDiagnosticsPlugin.getUnresolvedDependencies
- */
- override def getUnresolvedDependencies:Map[String, List[Dependency]] =
+ override def components:List[Comp] =
{
- // build a map(name -> comp) of all ComponentDeclarations known to DM, from each DependencyManager instance
- val comps:Map[String, ComponentDeclaration] =
- {
- (for(dm <- DependencyManager.getDependencyManagers;
- comp <- dm.asInstanceOf[DependencyManager].getComponents;
- cd = comp.asInstanceOf[ComponentDeclaration])
- // yield (k,v) builds a list of (k,v) pairs, the resulting list is then turned into a map
- // notice that toMap applies to the complete for-comprehension block
- // k is the ComponentDeclaration name stripped of its filter part: everything before the '('
- yield (cd.getName.takeWhile(_ != '('), cd)) toMap
- }
-
- val compNames = comps.keySet
-
- // build and return a map(name -> List(unavail)) of unavailable dependencies:
- // filter out registered services from the known ComponentDeclarations
- (for(kv <- comps.filter(kv => kv._2.getState == STATE_UNREGISTERED);
- // kv._1 is the name, kv._2 is the component
- // here, we lookup the component's list of dependencies
- // filtering out the ones available and the ones already known to DM, to keep only leafs
- // (view/force added for performance)
- unavail = kv._2.getComponentDependencies.view
- .filter(dep => dep.getState == STATE_UNAVAILABLE_REQUIRED)
- .filterNot(dep => compNames.contains(dep.getName))
- .map(dep => new Dependency(dep.getName, ""/*dep.getFilter*/)).force.toList;
- if (unavail nonEmpty)) //this 'if' is part of the for comprehension
- // again yield (k,v) builds a list of (k,v), the final result is then turned into a map
- // notice that toMap applies to the complete for-comprehension block
- // toMap being the last instruction, the resulting map is the return value of the method
- yield (kv._1, unavail)) toMap
+ // this involves a bit of type casting gymnastics because the underlying
+ // API does not use generic types
+ (for {
+ dm <- DependencyManager.getDependencyManagers.map(_.asInstanceOf[DependencyManager])
+ comp <- dm.getComponents.map(_.asInstanceOf[Component])
+ compdec = comp.asInstanceOf[ComponentDeclaration]
+ deps = compdec.getComponentDependencies
+ .map(dep => new Dependency(dep.getName.takeWhile(_ != ' '),
+ dep.getName.dropWhile(_ != ' ').trim,
+ dep.getState != STATE_UNAVAILABLE_REQUIRED)).toList
+ }
+ // yield Comp builds a list of Comp out of the for comprehension
+ yield new Comp(compdec.getName.takeWhile(_ != '('),
+ comp.getServiceProperties,
+ (compdec.getState != STATE_UNREGISTERED),
+ deps)) toList
}
-
}
diff --git a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/DSNotAvail.scala b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/DSNotAvail.scala
index 80101ed..f29885c 100644
--- a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/DSNotAvail.scala
+++ b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/DSNotAvail.scala
@@ -36,30 +36,25 @@
var scrService:ScrService = _ //dependency injection
- /**
- * implements ServiceDiagnosticsPlugin.getUnresolvedDependencies.
- */
- override def getUnresolvedDependencies:Map[String, List[Dependency]] =
+ def components:List[Comp] =
{
- /*
- * Same algorithm as DMNotAvail with the SCR API.
- * Please refer to DMNotAvail for comments.
- */
- val comps:Map[String, Component] =
- (for (c <- Option(scrService.getComponents).flatten;
- s <- Option(c.getServices).flatten)
- yield (s, c)).toMap
-
- val compNames = comps.keySet
-
- (for (kv <- comps.filter(kv => kv._2.getState == Component.STATE_UNSATISFIED);
- unavail = kv._2.getReferences.view
- .filterNot(_ isSatisfied)
- .filterNot(_ isOptional)
- .filterNot(ref => compNames.contains(ref.getName))
- .map(ref => new Dependency(ref.getServiceName, ref.getTarget)).force.toList;
- if (unavail nonEmpty))
- yield (kv._1, unavail)).toMap
+ // this involves a bit of type casting gymnastics because the underlying
+ // API uses mutables and no generic types
+ // Option is used to avoid null pointers
+ (for {
+ comp <- Option(scrService.getComponents).flatten.map(_.asInstanceOf[Component])
+ service <- Option(comp.getServices).flatten
+ deps = Option(comp.getReferences).flatten
+ .filterNot(_.isOptional)
+ .map(dep => new Dependency(dep.getServiceName,
+ dep.getTarget,
+ dep.isSatisfied)).toList
+ }
+ // yield Comp builds a list of Comp out of the for comprehension
+ yield new Comp(service,
+ comp.getProperties,
+ comp.getState != Component.STATE_UNSATISFIED,
+ deps)) toList
}
}
diff --git a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/ServiceDiagnosticsImpl.scala b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/ServiceDiagnosticsImpl.scala
index e4073af..00be218 100644
--- a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/ServiceDiagnosticsImpl.scala
+++ b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/impl/ServiceDiagnosticsImpl.scala
@@ -19,6 +19,7 @@
package org.apache.felix.servicediagnostics.impl
import scala.collection.mutable.Buffer
+import scala.collection.mutable.{Set => mSet}
import org.osgi.framework.BundleContext
import org.osgi.framework.ServiceReference
@@ -40,18 +41,85 @@
/**
* Implements ServiceDiagnostics.notavail.
*
- * This method gathers "notavail" information from all plugins
- * and performs the final merge by removing known unregistered services
+ * This method gathers components information from all plugins
+ * and filters all intermediate known unregistered services
+ * to keep only missing "leaf" dependencies
*/
- override def notavail =
+ override def notavail :Map[String, List[String]] =
{
- // merge all notavails from plugins
- // (kv stands for each key/value pair in a map)
- val merged = (for(p <- plugins; kv <- p.getUnresolvedDependencies) yield kv) toMap
+ val unavail :List[Comp] = for {
+ plugin <- plugins.toList
+ comp <- plugin.components
+ if (! comp.registered)
+ } yield comp
+ (for {
+ comp <- unavail
+ dep <- comp.deps.filterNot(_.available)
+ if (! unavail.exists(c => dep.matchedBy(c)))
+ } yield comp.toString -> comp.deps.filterNot(_.available).map(_.toString) ) toMap
- // remove remaining intermediates. ex: unresolved in DS -> unavailable in DM
- // and return the resulting map
- (for(kv <- merged; dep <- kv._2; if (merged.get(dep.name) isEmpty)) yield kv) toMap
+ }
+
+ class Node(val comp:Comp, val edges:mSet[Node] = mSet[Node]()) {
+ def name = comp.toString
+ override def toString = name + " -> " + edges.map(_.name)
+ }
+
+ /**
+ * returns a map of (component.name -> list(component.name)) of unresolvable services, if any
+ */
+ override def unresolved :Map[String, List[String]] =
+ {
+ // first build a traversable graph from all found component and dependencies
+ def buildGraph(link:(Node,Node)=>Unit) = {
+ // concatenate component nodes from all plugins
+ val allnodes = for ( p <- plugins; comp <- p.components ) yield new Node(comp)
+
+ // and connect the nodes according to component dependencies
+ // the 'link' method gives the direction of the link
+ for ( node <- allnodes; dep <- node.comp.deps )
+ {
+ allnodes.filter(n => dep.matchedBy(n.comp)).foreach(n => link(node, n) )
+ }
+
+ allnodes.toList //return the graph
+ }
+
+ // a "forward" graph of who depends on who
+ val graph = buildGraph((n1,n2) => n1.edges += n2)
+ // and the reverse graph of who "triggers" who
+ val triggers = buildGraph((n1,n2) => n2.edges += n1)
+
+ // recursive helper method used to traverse the graph and detect loops
+ def resolve(node:Node, visited:List[Node] = Nil) :List[Node] =
+ {
+ // if a node has no dependency, it is resolved
+ if (node.edges isEmpty) node::visited
+ else // replace ("map") each dependency with its resolution
+ {
+ val resolved = node.edges.map { e =>
+ if (visited contains e)
+ {
+ println("!!!LOOP {"+node.name+" -> "+e+"} in "+visited)
+ Nil // return an empty list
+ }
+ else resolve(e, node::visited)
+ }
+ if (resolved.contains(Nil)) Nil // there were some loops; resolution failed
+ else resolved.flatten.toList
+ }
+ }
+
+ // now traverse the graph starting from border nodes (nodes not pointed by anyone)
+ val resolved:Set[Node] = (for {
+ border <- triggers filter (_.edges.size == 0)
+ node <- graph.find(_.name == border.name)
+ } yield resolve(node)).flatten.toSet
+
+ // finally filter the original graph by removing all resolved nodes
+ // and format the result
+ (for (node <- graph.filterNot(n => resolved.contains(n)))
+ yield (node.name -> node.edges.map(_.name).toList)).toMap
}
/**
diff --git a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/webconsole/Servlet.scala b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/webconsole/Servlet.scala
index c26a3d7..2098669 100644
--- a/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/webconsole/Servlet.scala
+++ b/webconsole-plugins/servicediagnostics/core/src/main/scala/servicediagnostics/webconsole/Servlet.scala
@@ -25,6 +25,7 @@
import javax.servlet.http._
import org.json.JSONObject
+import org.json.JSONArray
import org.osgi.service.http.HttpContext
import org.osgi.service.http.HttpService
@@ -55,18 +56,20 @@
override def service(req:HttpServletRequest, resp:HttpServletResponse) =
{
val cmd = req.getPathInfo
- if(cmd.endsWith("all")) reply(resp, engine.allServices)
- else if(cmd.endsWith("notavail")) reply(resp, engine.notavail)
+ if(cmd.endsWith("all")) reply(resp, json(engine.allServices))
+ else if(cmd.endsWith("notavail")) reply(resp, new JSONObject()
+ .put("notavail", json(engine.notavail))
+ .put("unresolved", json(engine.unresolved)))
else println("Unrecognized cmd: "+cmd)
}
+ private def reply(resp:HttpServletResponse, json:JSONObject) =
+ new PrintStream(resp.getOutputStream, true).println(json)
+
/**
- * turn the ServiceDiagnostics output into a JSON representation.
- */
- private def reply(resp:HttpServletResponse, map:Map[String,List[AnyRef]]) =
- {
- new PrintStream(resp.getOutputStream, true).println(
- new JSONObject(asJavaMap(mMap() ++ map.map(kv => (kv._1, asJavaList(kv._2))))))
- }
+ * turn the ServiceDiagnostics output into a JSON representation.
+ */
+ private def json(map:Map[String,List[AnyRef]]) =
+ new JSONObject(asJavaMap(mMap() ++ map.map(kv => (kv._1, asJavaList(kv._2)))))
}
diff --git a/webconsole-plugins/servicediagnostics/parent/pom.xml b/webconsole-plugins/servicediagnostics/parent/pom.xml
index 346ab0e..c80395a 100644
--- a/webconsole-plugins/servicediagnostics/parent/pom.xml
+++ b/webconsole-plugins/servicediagnostics/parent/pom.xml
@@ -111,6 +111,15 @@
<extensions>true</extensions>
</plugin>
</plugins>
+
+ <!-- added for OBR deployment -->
+ <extensions>
+ <extension>
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-ssh</artifactId>
+ <version>1.0-beta-6</version>
+ </extension>
+ </extensions>
</build>
</project>
diff --git a/webconsole-plugins/servicediagnostics/run.sh b/webconsole-plugins/servicediagnostics/run.sh
index f7b74b5..82f8f88 100755
--- a/webconsole-plugins/servicediagnostics/run.sh
+++ b/webconsole-plugins/servicediagnostics/run.sh
@@ -1,14 +1,22 @@
REPO=$HOME/.m2/repository
-CLASSPATH=$REPO/org/scala-lang/scala-library/2.9.1/scala-library-2.9.1.jar:$REPO/org/apache/felix/org.apache.felix.main/3.2.2/org.apache.felix.main-3.2.2.jar:sample/target/servicediagnostics.sample-0.1.0-SNAPSHOT.jar
+SCALA=$REPO/org/apache/servicemix/bundles/org.apache.servicemix.bundles.scala-library/2.9.1_3/org.apache.servicemix.bundles.scala-library-2.9.1_3.jar
+CLASSPATH=$SCALA:$REPO/org/apache/felix/org.apache.felix.main/4.0.3/org.apache.felix.main-4.0.3.jar:sample/target/servicediagnostics.sample-0.1.1-SNAPSHOT.jar
#scala
java -classpath $CLASSPATH org.apache.felix.servicediagnostics.sample.FelixLauncher \
- core/target/org.apache.felix.servicediagnostics.plugin-0.1.0-SNAPSHOT.jar\
- sample/target/servicediagnostics.sample-0.1.0-SNAPSHOT.jar\
- $REPO/org/apache/felix/org.apache.felix.main/3.2.2/org.apache.felix.main-3.2.2.jar\
+ $SCALA\
+ core/target/org.apache.felix.servicediagnostics.plugin-0.1.2-SNAPSHOT.jar\
+ sample/target/servicediagnostics.sample-0.1.1-SNAPSHOT.jar\
+ $REPO/org/apache/felix/org.apache.felix.main/4.0.3/org.apache.felix.main-4.0.3.jar\
$REPO/org/apache/felix/org.apache.felix.dependencymanager/3.0.0/org.apache.felix.dependencymanager-3.0.0.jar\
+ $REPO/org/apache/felix/org.apache.felix.dependencymanager.shell/3.0.0/org.apache.felix.dependencymanager.shell-3.0.0.jar\
$REPO/org/apache/felix/org.apache.felix.scr/1.6.0/org.apache.felix.scr-1.6.0.jar\
$REPO/org/osgi/org.osgi.compendium/4.2.0/org.osgi.compendium-4.2.0.jar\
$REPO/org/apache/felix/org.apache.felix.http.jetty/2.2.0/org.apache.felix.http.jetty-2.2.0.jar\
- $REPO/org/apache/felix/org.apache.felix.webconsole/3.1.8/org.apache.felix.webconsole-3.1.8.jar\
- $REPO/org/apache/felix/org.apache.felix.shell/1.4.2/org.apache.felix.shell-1.4.2.jar
+ $REPO/org/apache/felix/org.apache.felix.webconsole/4.0.0/org.apache.felix.webconsole-4.0.0.jar\
+ $REPO/org/apache/felix/org.apache.felix.shell/1.4.3/org.apache.felix.shell-1.4.3.jar\
+ $REPO/org/apache/commons/com.springsource.org.apache.commons.fileupload/1.2.1/com.springsource.org.apache.commons.fileupload-1.2.1.jar\
+ $REPO/org/apache/commons/com.springsource.org.apache.commons.io/1.4.0/com.springsource.org.apache.commons.io-1.4.0.jar\
+ $REPO/org/apache/geronimo/bundles/json/20090211_1/json-20090211_1.jar\
+ $REPO/org/apache/felix/org.apache.felix.webconsole.plugins.shell/1.0.0-SNAPSHOT/org.apache.felix.webconsole.plugins.shell-1.0.0-SNAPSHOT.jar
+
diff --git a/webconsole-plugins/servicediagnostics/sample/pom.xml b/webconsole-plugins/servicediagnostics/sample/pom.xml
index 1929970..164e6ce 100644
--- a/webconsole-plugins/servicediagnostics/sample/pom.xml
+++ b/webconsole-plugins/servicediagnostics/sample/pom.xml
@@ -6,7 +6,7 @@
<parent>
<groupId>org.apache.felix</groupId>
<artifactId>servicediagnostics.parent</artifactId>
- <version>0.1.1-SNAPSHOT</version>
+ <version>0.1.1</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@@ -16,17 +16,60 @@
<packaging>bundle</packaging>
<name>Sample Services and Launcher for Service Diagnostics</name>
+ <repositories>
+ <repository>
+ <id>com.springsource.repository.bundles.release</id>
+ <name>SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases</name>
+ <url>http://repository.springsource.com/maven/bundles/release</url>
+ </repository>
+ <repository>
+ <id>com.springsource.repository.bundles.external</id>
+ <name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
+ <url>http://repository.springsource.com/maven/bundles/external</url>
+ </repository>
+ </repositories>
+
<dependencies>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>${project.groupId}.servicediagnostics.plugin</artifactId>
- <version>0.1.0-SNAPSHOT</version>
+ <version>0.1.2-SNAPSHOT</version>
</dependency>
<!-- runtime dependencies. added to populate the local repository -->
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.webconsole.plugins.shell</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.webconsole.plugins.ds</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>com.springsource.org.apache.commons.fileupload</artifactId>
+ <version>1.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>com.springsource.org.apache.commons.io</artifactId>
+ <version>1.4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.bundles</groupId>
+ <artifactId>json</artifactId>
+ <version>20090211_1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.dependencymanager.shell</artifactId>
+ <version>3.0.0</version>
+ </dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.main</artifactId>
- <version>3.2.2</version>
+ <version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
@@ -36,13 +79,12 @@
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.shell</artifactId>
- <version>1.4.2</version>
+ <version>1.4.3</version>
</dependency>
<dependency>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- <version>2.9.1</version>
- <scope>provided</scope>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.scala-library</artifactId>
+ <version>2.9.1_3</version>
</dependency>
</dependencies>
@@ -60,6 +102,7 @@
<instructions>
<Bundle-Category>samples</Bundle-Category>
<Bundle-SymbolicName> ${project.artifactId} </Bundle-SymbolicName>
+ <Bundle-Activator> org.apache.felix.servicediagnostics.sample.TestDM </Bundle-Activator>
<Service-Component> * </Service-Component>
<Private-Package>
org.apache.felix.servicediagnostics.sample
@@ -68,11 +111,8 @@
!aQute.bnd.annotation.component,!org.apache.felix.main,sun.misc*;resolution:=optional,*
</Import-Package>
<Include-Resource>
- {maven-resources},scala-library-2.9.1.jar
+ {maven-resources}
</Include-Resource>
- <Bundle-ClassPath>
- ., scala-library-2.9.1.jar
- </Bundle-ClassPath>
</instructions>
</configuration>
</plugin>
diff --git a/webconsole-plugins/servicediagnostics/sample/src/main/scala/servicediagnostics/sample/TestDM.scala b/webconsole-plugins/servicediagnostics/sample/src/main/scala/servicediagnostics/sample/TestDM.scala
index d56b364..f0dde6b 100644
--- a/webconsole-plugins/servicediagnostics/sample/src/main/scala/servicediagnostics/sample/TestDM.scala
+++ b/webconsole-plugins/servicediagnostics/sample/src/main/scala/servicediagnostics/sample/TestDM.scala
@@ -23,8 +23,11 @@
import org.apache.felix.dm.DependencyManager
import org.apache.felix.servicediagnostics.ServiceDiagnostics
+import java.util. { Hashtable => jHT }
+
/**
* This class is a basic DependencyManager based demonstration
+ *
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
@@ -48,11 +51,16 @@
.setRequired(true)))
// initialize some sample services for testing purpose (see also TestDS)
+
+ //
+ // test1:
+ // DM1 -> DS1 -> DM2 -> Unavail
+
dm.add(createComponent
.setInterface(classOf[DM1].getName, null)
.setImplementation(classOf[DM1])
.add(createServiceDependency
- .setService(classOf[DM2])
+ .setService(classOf[DS1])
.setAutoConfig(false)
.setCallbacks(null, null, null)
.setRequired(true)))
@@ -61,16 +69,65 @@
.setInterface(classOf[DM2].getName, null)
.setImplementation(classOf[DM2])
.add(createServiceDependency
- .setService(classOf[DM3])
+ .setService(classOf[Runnable]) //Unavail
+ .setAutoConfig(false)
+ .setCallbacks(null, null, null)
+ .setRequired(true)))
+
+ // test2: properties
+ // DMP(0,1) -> DSP(0) -> DMP(2) -> Unavail
+
+ dm.add(createComponent
+ .setInterface(classOf[DMP].getName, new jHT[String,String]() {{
+ put("p", "0")
+ put("q", "1")
+ }})
+ .setImplementation(classOf[DMP])
+ .add(createServiceDependency
+ .setService(classOf[DSP], "(p=0)")
.setAutoConfig(false)
.setCallbacks(null, null, null)
.setRequired(true)))
dm.add(createComponent
- .setInterface(classOf[DM3].getName, null)
- .setImplementation(classOf[DM3])
+ .setInterface(classOf[DMP].getName, new jHT[String,String]() {{
+ put("p", "2")
+ }})
+ .setImplementation(classOf[DMP])
.add(createServiceDependency
- .setService(classOf[Runnable]) //missing dependency
+ .setService(classOf[Runnable]) // Unavail
+ .setAutoConfig(false)
+ .setCallbacks(null, null, null)
+ .setRequired(true)))
+
+ // test3: loop
+ // DML1 -> DSL1 -> DML2 -> DML1
+
+ dm.add(createComponent
+ .setInterface(classOf[DML1].getName, new jHT[String,String]() {{
+ put("p", "1")
+ put("q", "1")
+ }})
+ .setImplementation(classOf[DML1])
+ .add(createServiceDependency
+ .setService(classOf[DSL1], "(q=1)")
+ .setAutoConfig(false)
+ .setCallbacks(null, null, null)
+ .setRequired(true)))
+
+ dm.add(createComponent
+ .setInterface(classOf[DML2].getName, new jHT[String,String]() {{
+ put("p", "2")
+ put("q", "2")
+ }})
+ .setImplementation(classOf[DML2])
+ .add(createServiceDependency
+ .setService(classOf[DML1]) //Loop
+ .setAutoConfig(false)
+ .setCallbacks(null, null, null)
+ .setRequired(true))
+ .add(createServiceDependency
+ .setService(classOf[TestDS]) //Available with no deps
.setAutoConfig(false)
.setCallbacks(null, null, null)
.setRequired(true)))
@@ -78,14 +135,20 @@
override def destroy(bc:BundleContext, dm:DependencyManager) = {}
- def start =
+ def start = try
{
println("unavail="+diagnostics.notavail)
println("all="+diagnostics.allServices)
}
+ catch
+ {
+ case (e:Exception) => e.printStackTrace
+ }
}
//sample service classes
class DM1
class DM2
-class DM3
+class DMP
+class DML1
+class DML2
diff --git a/webconsole-plugins/servicediagnostics/sample/src/main/scala/servicediagnostics/sample/TestDS.scala b/webconsole-plugins/servicediagnostics/sample/src/main/scala/servicediagnostics/sample/TestDS.scala
index 57adf9b..1a830cf 100644
--- a/webconsole-plugins/servicediagnostics/sample/src/main/scala/servicediagnostics/sample/TestDS.scala
+++ b/webconsole-plugins/servicediagnostics/sample/src/main/scala/servicediagnostics/sample/TestDS.scala
@@ -27,17 +27,36 @@
*/
@Component(immediate=true, provide=Array(classOf[TestDS])) class TestDS
-@Component(provide=Array(classOf[DS1])) class DS1
+// DM1 -> DS1 -> DM2 -> Unavail
+@Component(provide=Array(classOf[DS1]))
+class DS1
{
- @Reference def setDS2(s:DS2) = {}
+ @Reference def bind(s:DM2) = {}
}
-@Component(provide=Array(classOf[DS2])) class DS2
+// DMP(0,1) -> DSP(0) -> DMP(2) -> Unavail
+@Component(provide=Array(classOf[DSP]), properties=Array("p=0"))
+class DSP
{
- @Reference def setDM3(s:DM3) = {}
+ @Reference(target="(p=2)") def bind(s:DMP) = {}
}
-@Component(provide=Array(classOf[DS3])) class DS3
+// DML1 -> DSL1 -> DML2 -> DML1
+@Component(provide=Array(classOf[DSL1]), properties=Array("p=0","q=1"))
+class DSL1
{
- @Reference def setMap(s:java.util.Map[String,String]) = {} //not available
+ @Reference(target="(p=2)") def bind(s:DML2) = {}
}
+
+/* DSL2 -> DML2 -x-> DSL3 -> DSL2
+@Component(provide=Array(classOf[DSL2]), properties=Array("p=1","q=0"))
+class DSL2
+{
+ @Reference(target="(p=2)") def bind(s:DML2) = {}
+}
+@Component(provide=Array(classOf[DSL3]), properties=Array("p=1","q=0"))
+class DSL3
+{
+ @Reference(target="(q=0)") def bind(s:DSL2) = {}
+}*/
+