FELIX-2264 Implement for lazy service registration and dynamic API binding
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@931858 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole-plugins/memoryusage/pom.xml b/webconsole-plugins/memoryusage/pom.xml
index c99ddac..921af12 100644
--- a/webconsole-plugins/memoryusage/pom.xml
+++ b/webconsole-plugins/memoryusage/pom.xml
@@ -71,20 +71,24 @@
</Bundle-SymbolicName>
<Import-Package>
javax.management.*,
- org.osgi.framework,
- org.slf4j.*,
+ org.osgi.framework
+ </Import-Package>
+ <DynamicImport-Package>
+ <!-- logging -->
+ org.osgi.service.log;version="[1.3,2)",
- <!-- plug into the traditional Felix shell -->
- org.apache.felix.shell;
+ <!-- configuration -->
+ org.osgi.service.cm;version="[1.2,2)",
+ org.osgi.service.metatype;version="[1.1,2)",
<!-- plug into the web console -->
- javax.servlet.*;
- org.apache.felix.webconsole;
+ javax.servlet;
+ javax.servlet.http;version="[2.3,3)",
+ org.apache.felix.webconsole;version="[3.0,3.1)",
- <!-- support configuration -->
- org.osgi.service.cm;
- org.osgi.service.metatype;resolution:=optional
- </Import-Package>
+ <!-- plug into the traditional Felix shell -->
+ org.apache.felix.shell;version="[1.0,1.1)"
+ </DynamicImport-Package>
<Private-Package>
org.apache.felix.webconsole.plugins.memoryusage.*
</Private-Package>
@@ -127,16 +131,5 @@
<version>2.3</version>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.5.2</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- </dependency>
</dependencies>
</project>
diff --git a/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/Activator.java b/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/Activator.java
index 4b18d68..1b4ee5d 100644
--- a/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/Activator.java
+++ b/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/Activator.java
@@ -21,59 +21,56 @@
import java.util.Dictionary;
import java.util.Hashtable;
+import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
public class Activator implements BundleActivator
{
private MemoryUsageSupport support;
- public void start(BundleContext bundleContext)
+ public void start(final BundleContext bundleContext)
{
-
support = new MemoryUsageSupport(bundleContext);
// install thread handler shell command
- try
+ new AbstractServiceFactory(bundleContext, null, "org.apache.felix.shell.Command")
{
- register(bundleContext, new String[]
- { "org.apache.felix.shell.Command" }, new MemoryUsageCommand(support), null);
- }
- catch (Throwable t)
- {
- // shell service might not be available, don't care
- }
+ @Override
+ protected Object createObject()
+ {
+ return new MemoryUsageCommand(support);
+ }
+ };
// install Web Console plugin
- try
+ Dictionary<String, Object> pluginProps = new Hashtable<String, Object>();
+ pluginProps.put("felix.webconsole.label", MemoryUsageConstants.LABEL);
+ new AbstractServiceFactory(bundleContext, pluginProps, "javax.servlet.Servlet",
+ "org.apache.felix.webconsole.ConfigurationPrinter")
{
- Dictionary<String, Object> properties = new Hashtable<String, Object>();
- properties.put("felix.webconsole.label", MemoryUsageConstants.LABEL);
-
- register(bundleContext, new String[]
- { "javax.servlet.Servlet", "org.apache.felix.webconsole.ConfigurationPrinter" }, new MemoryUsagePanel(
- bundleContext, support), properties);
- }
- catch (Throwable t)
- {
- // web console might not be available, don't care
- }
+ @Override
+ public Object createObject()
+ {
+ return new MemoryUsagePanel(support);
+ }
+ };
// register for configuration
- try
+ Dictionary<String, Object> cmProps = new Hashtable<String, Object>();
+ cmProps.put(Constants.SERVICE_PID, MemoryUsageConstants.PID);
+ new AbstractServiceFactory(bundleContext, cmProps, "org.osgi.service.cm.ManagedService")
{
- Dictionary<String, Object> properties = new Hashtable<String, Object>();
- properties.put(Constants.SERVICE_PID, MemoryUsageConstants.PID);
- register(bundleContext, new String[]
- { "org.osgi.service.cm.ManagedService" }, new MemoryUsageConfigurator(support), properties);
- }
- catch (Throwable t)
- {
- // Configuration Admin and Metatype Service API might not be
- // available, don't care
- }
+ @Override
+ public Object createObject()
+ {
+ return new MemoryUsageConfigurator(support);
+ }
+ };
}
public void stop(BundleContext bundleContext)
@@ -85,21 +82,46 @@
}
}
- static void register(BundleContext context, String[] serviceNames, Object service,
- Dictionary<String, Object> properties)
+ private static abstract class AbstractServiceFactory implements ServiceFactory
{
+ private int counter;
+ private Object service;
- // ensure properties
- if (properties == null)
+ public AbstractServiceFactory(BundleContext context, Dictionary<String, Object> properties,
+ String... serviceNames)
{
- properties = new Hashtable<String, Object>();
+ // ensure properties
+ if (properties == null)
+ {
+ properties = new Hashtable<String, Object>();
+ }
+
+ // default settings
+ properties.put(Constants.SERVICE_DESCRIPTION, "Memory Usage (" + serviceNames[0] + ")");
+ properties.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
+
+ context.registerService(serviceNames, this, properties);
}
- // default settings
- properties.put(Constants.SERVICE_DESCRIPTION, "Memory Usage (" + serviceNames[0] + ")");
- properties.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
+ public synchronized void ungetService(Bundle bundle, ServiceRegistration registration, Object service)
+ {
+ counter--;
+ if (counter <= 0)
+ {
+ service = null;
+ }
+ }
- context.registerService(serviceNames, service, properties);
+ public synchronized Object getService(Bundle bundle, ServiceRegistration registration)
+ {
+ counter++;
+ if (service == null)
+ {
+ service = createObject();
+ }
+ return service;
+ }
+
+ protected abstract Object createObject();
}
-
}
diff --git a/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/MemoryUsagePanel.java b/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/MemoryUsagePanel.java
index 88e7258..8af03ca 100644
--- a/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/MemoryUsagePanel.java
+++ b/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/MemoryUsagePanel.java
@@ -41,22 +41,18 @@
import org.apache.felix.webconsole.DefaultVariableResolver;
import org.apache.felix.webconsole.WebConsoleUtil;
import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.osgi.service.log.LogService;
@SuppressWarnings("serial")
class MemoryUsagePanel extends AbstractWebConsolePlugin implements ConfigurationPrinter, AttachmentProvider
{
- /** default log */
- private final Logger log = LoggerFactory.getLogger(getClass());
-
private final MemoryUsageSupport support;
- MemoryUsagePanel(final BundleContext bundleContext, final MemoryUsageSupport support)
+ MemoryUsagePanel(final MemoryUsageSupport support)
{
this.support = support;
- activate(bundleContext);
+ activate(support.getBundleContext());
}
// ---------- AbstractWebConsolePlugin
@@ -198,7 +194,7 @@
{
resp.getWriter().print(
"Failed dumping the heap, JVM does not provide known mechanism to create a Heap Dump");
- log.error("Heap Dump creation failed: JVM has no known Heap Dump API");
+ support.log(LogService.LOG_ERROR, "Heap Dump creation failed: JVM has no known Heap Dump API");
}
}
else if ("gc".equals(command))
diff --git a/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/MemoryUsageSupport.java b/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/MemoryUsageSupport.java
index 705c419..5e65893 100644
--- a/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/MemoryUsageSupport.java
+++ b/webconsole-plugins/memoryusage/src/main/java/org/apache/felix/webconsole/plugins/memoryusage/internal/MemoryUsageSupport.java
@@ -19,6 +19,7 @@
package org.apache.felix.webconsole.plugins.memoryusage.internal;
import java.io.File;
+import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryNotificationInfo;
@@ -41,16 +42,20 @@
import javax.management.ObjectName;
import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
-final class MemoryUsageSupport implements NotificationListener
+final class MemoryUsageSupport implements NotificationListener, ServiceListener
{
// This is the name of the HotSpot Diagnostic MBean
private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic";
- private final Logger log = LoggerFactory.getLogger(getClass());
+ // to get the LogService
+ private final BundleContext context;
// the default dump location: the dumps folder in the bundle private data
// or the current working directory
@@ -65,8 +70,28 @@
// the actual threshold (configured or dynamically set in the console UI)
private int threshold;
+ // log service
+ private ServiceReference logServiceReference;
+ private Object logService;
+
MemoryUsageSupport(final BundleContext context)
{
+ this.context = context;
+
+ // register for the log service
+ try
+ {
+ context.addServiceListener(this, "(objectclass=org.osgi.service.log.LogService)");
+ logServiceReference = context.getServiceReference("org.osgi.service.log.LogService");
+ if (logServiceReference != null)
+ {
+ logService = context.getService(logServiceReference);
+ }
+ }
+ catch (InvalidSyntaxException ise)
+ {
+ // TODO
+ }
// the default dump location
String propDumps = context.getProperty(MemoryUsageConstants.PROP_DUMP_LOCATION);
@@ -139,6 +164,19 @@
{
// don't care
}
+
+ context.removeServiceListener(this);
+ if (logServiceReference != null)
+ {
+ context.ungetService(logServiceReference);
+ logServiceReference = null;
+ logService = null;
+ }
+ }
+
+ public BundleContext getBundleContext()
+ {
+ return context;
}
/**
@@ -180,8 +218,9 @@
}
this.threshold = percentage;
- log.info("Setting Automatic Memory Dump Threshold to {}% for pools {}", threshold, thresholdPools);
- log.info("Automatic Memory Dump cannot be set for pools {}", noThresholdPools);
+ log(LogService.LOG_INFO, "Setting Automatic Memory Dump Threshold to %d%% for pools %s", threshold,
+ thresholdPools);
+ log(LogService.LOG_INFO, "Automatic Memory Dump cannot be set for pools %s", noThresholdPools);
}
else
{
@@ -351,7 +390,7 @@
this.dumpLocation = new File(dumpLocation).getAbsoluteFile();
}
- log.info("Storing Memory Dumps in {}", this.dumpLocation);
+ log(LogService.LOG_INFO, "Storing Memory Dumps in %s", this.dumpLocation);
}
final File getDumpLocation()
@@ -463,15 +502,16 @@
String notifType = notification.getType();
if (notifType.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED))
{
- log.warn("Received Memory Threshold Exceed Notification, dumping Heap");
+ log(LogService.LOG_WARNING, "Received Memory Threshold Exceed Notification, dumping Heap");
try
{
File file = dumpHeap(null, true);
- log.warn("Heap dumped to " + file);
+ log(LogService.LOG_WARNING, "Heap dumped to " + file);
}
catch (NoSuchElementException e)
{
- log.error("Failed dumping the heap, JVM does not provide known mechanism to create a Heap Dump");
+ log(LogService.LOG_ERROR,
+ "Failed dumping the heap, JVM does not provide known mechanism to create a Heap Dump");
}
}
}
@@ -507,12 +547,12 @@
{ tmpFile.getAbsolutePath(), live }, new String[]
{ String.class.getName(), boolean.class.getName() });
- log.debug("dumpSunMBean: Dumped Heap to {} using Sun HotSpot MBean", tmpFile);
+ log(LogService.LOG_DEBUG, "dumpSunMBean: Dumped Heap to %s using Sun HotSpot MBean", tmpFile);
return tmpFile;
}
catch (Throwable t)
{
- log.debug("dumpSunMBean: Dump by Sun HotSpot MBean not working", t);
+ log(LogService.LOG_DEBUG, "dumpSunMBean: Dump by Sun HotSpot MBean not working", t);
tmpFile.delete();
}
@@ -522,9 +562,7 @@
/**
* @param name
* @return
- * @see http
- * ://publib.boulder.ibm.com/infocenter/javasdk/v5r0/index.jsp?topic
- * =/com.ibm.java.doc.diagnostics.50/diag/tools/heapdump_enable.html
+ * @see <a href="http://publib.boulder.ibm.com/infocenter/javasdk/v5r0/index.jsp?topic=/com.ibm.java.doc.diagnostics.50/diag/tools/heapdump_enable.html">Getting Heapdumps</a>
*/
private File dumpIbmDump(String name)
{
@@ -554,27 +592,87 @@
File target = new File(dumpLocation, name);
file.renameTo(target);
- log.debug("dumpSunMBean: Dumped Heap to {} using IBM Dump.HeapDump()", target);
+ log(LogService.LOG_DEBUG, "dumpSunMBean: Dumped Heap to %s using IBM Dump.HeapDump()", target);
return target;
}
}
- log.debug("dumpIbmDump: None of {} files '{}' is younger than {}", new Object[]
- { files.length, dir, minFileTime });
+ log(LogService.LOG_DEBUG, "dumpIbmDump: None of %d files '%s' is younger than %d", files.length, dir,
+ minFileTime);
}
else
{
- log.debug("dumpIbmDump: Hmm '{}' does not seem to be a directory; isdir={} ??", dir, dir.isDirectory());
+ log(LogService.LOG_DEBUG, "dumpIbmDump: Hmm '%s' does not seem to be a directory; isdir=%b ??", dir,
+ dir.isDirectory());
}
- log.warn("dumpIbmDump: Heap Dump has been created but cannot be located");
+ log(LogService.LOG_WARNING, "dumpIbmDump: Heap Dump has been created but cannot be located");
return dumpLocation;
}
catch (Throwable t)
{
- log.debug("dumpIbmDump: Dump by IBM Dump class not working", t);
+ log(LogService.LOG_DEBUG, "dumpIbmDump: Dump by IBM Dump class not working", t);
}
return null;
}
+
+ // ---------- Logging support
+
+ public void serviceChanged(ServiceEvent event)
+ {
+ if (event.getType() == ServiceEvent.REGISTERED && logServiceReference == null)
+ {
+ logServiceReference = event.getServiceReference();
+ logService = context.getService(event.getServiceReference());
+ }
+ else if (event.getType() == ServiceEvent.UNREGISTERING && logServiceReference == event.getServiceReference())
+ {
+ logServiceReference = null;
+ logService = null;
+ context.ungetService(event.getServiceReference());
+ }
+ }
+
+ void log(int level, String format, Object... args)
+ {
+ log(level, null, format, args);
+ }
+
+ void log(int level, Throwable t, String format, Object... args)
+ {
+ Object logService = this.logService;
+ final String message = String.format(format, args);
+ if (logService != null)
+ {
+ ((LogService) logService).log(level, message, t);
+ }
+ else
+ {
+ PrintStream out = (level <= LogService.LOG_ERROR) ? System.err : System.out;
+ out.printf("%s: %s (%d): %s%n", toLevelString(level), context.getBundle().getSymbolicName(), context
+ .getBundle().getBundleId(), message);
+ if (t != null)
+ {
+ t.printStackTrace(out);
+ }
+ }
+ }
+
+ private String toLevelString(int level)
+ {
+ switch (level)
+ {
+ case LogService.LOG_DEBUG:
+ return "DEBUG";
+ case LogService.LOG_INFO:
+ return "INFO ";
+ case LogService.LOG_WARNING:
+ return "WARN ";
+ case LogService.LOG_ERROR:
+ return "ERROR";
+ default:
+ return "unknown(" + level + ")";
+ }
+ }
}