Applied a patch (FELIX-804) to add partial support for new service registry
hooks as proposed for OSGi R4.2; currently, only listener hooks are supported.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@714075 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/pom.xml b/framework/pom.xml
index db2fc6a..cf4d3d1 100644
--- a/framework/pom.xml
+++ b/framework/pom.xml
@@ -62,7 +62,7 @@
<Bundle-Name>Apache Felix Framework</Bundle-Name>
<Bundle-Description>OSGi R4 framework implementation.</Bundle-Description>
<Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
- <Export-Package>org.osgi.framework;-split-package:=merge-first,org.osgi.framework.launch,org.osgi.service.packageadmin,org.osgi.service.url,org.osgi.service.startlevel,org.osgi.util.tracker</Export-Package>
+ <Export-Package>org.osgi.framework;-split-package:=merge-first,org.osgi.framework.launch,org.osgi.framework.hooks.service,org.osgi.service.packageadmin,org.osgi.service.url,org.osgi.service.startlevel,org.osgi.util.tracker</Export-Package>
<Private-Package>org.apache.felix.moduleloader.*,org.apache.felix.framework.*</Private-Package>
<Import-Package>!*</Import-Package>
<Include-Resource>META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,{src/main/resources/},org/osgi/framework/=target/classes/org/osgi/framework/</Include-Resource>
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 973c4f8..c640a75 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -31,6 +31,7 @@
import org.apache.felix.framework.util.manifestparser.*;
import org.apache.felix.moduleloader.*;
import org.osgi.framework.*;
+import org.osgi.framework.hooks.service.ListenerHook;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.startlevel.StartLevel;
@@ -2625,6 +2626,14 @@
{
m_dispatcher.addListener(
bundle, ServiceListener.class, l, (f == null) ? null : new FilterImpl(m_logger, f));
+
+ // Invoke the ListenerHook.added() on all hooks.
+ ListenerHook[] hooks = m_registry.getListenerHooks();
+ Collection c = Collections.singleton(new ListenerHookInfoImpl(bundle.getBundleContext(), f));
+ for (int i = 0; i < hooks.length; i++)
+ {
+ hooks[i].added(c);
+ }
}
/**
@@ -2636,7 +2645,19 @@
**/
protected void removeServiceListener(Bundle bundle, ServiceListener l)
{
- m_dispatcher.removeListener(bundle, ServiceListener.class, l);
+ ListenerHook.ListenerInfo listener =
+ m_dispatcher.removeListener(bundle, ServiceListener.class, l);
+
+ if (listener != null)
+ {
+ // Invoke the ListenerHook.removed() on all hooks.
+ ListenerHook[] hooks = m_registry.getListenerHooks();
+ Collection c = Collections.singleton(listener);
+ for (int i = 0; i < hooks.length; i++)
+ {
+ hooks[i].removed(c);
+ }
+ }
}
protected void addFrameworkListener(Bundle bundle, FrameworkListener l)
@@ -2718,6 +2739,14 @@
releaseBundleLock(bundle);
}
+ // Check to see if this a listener hook; if so, then we need
+ // to invoke the callback with all existing service listeners.
+ if (m_registry.isHook(classNames, ListenerHook.class, svcObj))
+ {
+ ListenerHook lHook = (ListenerHook) svcObj;
+ lHook.added(m_dispatcher.wrapAllServiceListeners());
+ }
+
// TODO: CONCURRENCY - Reconsider firing event here, outside of the
// bundle lock.
diff --git a/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java b/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
index c338830..47073bb 100644
--- a/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ServiceRegistrationImpl.java
@@ -167,6 +167,17 @@
return m_registry.getUsingBundles(m_ref);
}
+ /**
+ * This method provides direct access to the associated service object;
+ * it generally should not be used by anyone other than the service registry
+ * itself.
+ * @return The service object associated with the registration.
+ **/
+ Object getService()
+ {
+ return m_svcObj;
+ }
+
protected Object getService(Bundle acqBundle)
{
// If the service object is a service factory, then
diff --git a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
index 200b633..03e87b0 100644
--- a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
+++ b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
@@ -22,6 +22,7 @@
import org.apache.felix.framework.util.FelixConstants;
import org.osgi.framework.*;
+import org.osgi.framework.hooks.service.ListenerHook;
public class ServiceRegistry
{
@@ -38,6 +39,8 @@
private ServiceListener m_serviceListener = null;
+ private final List m_listenerHooks = new ArrayList();
+
public ServiceRegistry(Logger logger)
{
m_logger = logger;
@@ -65,6 +68,9 @@
synchronized (this)
{
+ // Keep track of registered hooks.
+ addHooks(classNames, svcObj);
+
// Create the service registration.
reg = new ServiceRegistrationImpl(
this, bundle, classNames, new Long(m_currentServiceId++), svcObj, dict);
@@ -78,6 +84,9 @@
public void unregisterService(Bundle bundle, ServiceRegistration reg)
{
+ // If this is a hook, it should be removed.
+ removeHook(((ServiceRegistrationImpl) reg).getService());
+
synchronized (this)
{
// Note that we don't lock the service registration here using
@@ -426,7 +435,7 @@
{
usages = (UsageCount[]) m_inUseMap.get(bundle);
}
-
+
if (usages == null)
{
return;
@@ -714,6 +723,53 @@
}
}
+ private void addHooks(String[] classNames, Object svcObj)
+ {
+ if (isHook(classNames, ListenerHook.class, svcObj))
+ {
+ synchronized (m_listenerHooks)
+ {
+ m_listenerHooks.add(svcObj);
+ }
+ }
+ }
+
+ boolean isHook(String[] classNames, Class hookClass, Object svcObj)
+ {
+ if (hookClass.isAssignableFrom(svcObj.getClass()))
+ {
+ String hookName = hookClass.getName();
+ for (int i = 0; i < classNames.length; i++)
+ {
+ if (classNames[i].equals(hookName))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void removeHook(Object svcObj)
+ {
+ if (svcObj instanceof ListenerHook)
+ {
+ synchronized (m_listenerHooks)
+ {
+ m_listenerHooks.remove(svcObj);
+ }
+ }
+ }
+
+ ListenerHook[] getListenerHooks()
+ {
+ synchronized (m_listenerHooks)
+ {
+ return (ListenerHook[])
+ m_listenerHooks.toArray(new ListenerHook[m_listenerHooks.size()]);
+ }
+ }
+
private static class UsageCount
{
public int m_count = 0;
diff --git a/framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java b/framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
index 2774c26..8b3a565 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
@@ -21,9 +21,11 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.EventListener;
import java.util.EventObject;
+import java.util.List;
import org.apache.felix.framework.Logger;
import org.osgi.framework.AllServiceListener;
import org.osgi.framework.Bundle;
@@ -38,6 +40,7 @@
import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.framework.hooks.service.ListenerHook;
public class EventDispatcher
{
@@ -93,9 +96,9 @@
{
EventDispatcher.run();
}
- finally
+ finally
{
- // Ensure we update state even if stopped by external cause
+ // Ensure we update state even if stopped by external cause
// e.g. an Applet VM forceably killing threads
synchronized (m_threadLock)
{
@@ -265,8 +268,11 @@
}
}
- public void removeListener(Bundle bundle, Class clazz, EventListener l)
+ public ListenerHook.ListenerInfo removeListener(
+ Bundle bundle, Class clazz, EventListener l)
{
+ ListenerHook.ListenerInfo listenerInfo = null;
+
// Verify listener.
if (l == null)
{
@@ -315,6 +321,12 @@
(listeners[i + LISTENER_CLASS_OFFSET] == clazz) &&
(listeners[i + LISTENER_OBJECT_OFFSET] == l))
{
+ // For service listeners, we must return some info about
+ // the listener for the ListenerHook callback.
+ if (ServiceListener.class == clazz)
+ {
+ listenerInfo = wrapListener(listeners, i);
+ }
idx = i;
break;
}
@@ -367,6 +379,10 @@
m_serviceListeners = listeners;
}
}
+
+ // Return information about the listener; this is null
+ // for everything but service listeners.
+ return listenerInfo;
}
public void removeListeners(Bundle bundle)
@@ -504,6 +520,45 @@
return false;
}
+ /**
+ * Returns all existing service listener information into a collection of
+ * ListenerHook.ListenerInfo objects. This is used the first time a listener
+ * hook is registered to synchronize it with the existing set of listeners.
+ * @return Returns all existing service listener information into a collection of
+ * ListenerHook.ListenerInfo objects
+ **/
+ public Collection /* <? extends ListenerHook.ListenerInfo> */ wrapAllServiceListeners()
+ {
+ Object[] listeners = null;
+ synchronized (this)
+ {
+ listeners = m_serviceListeners;
+ }
+
+ List existingListeners = new ArrayList();
+ for (int i = 0, j = 0; i < listeners.length; i += LISTENER_ARRAY_INCREMENT, j++)
+ {
+ existingListeners.add(wrapListener(listeners, i));
+ }
+ return existingListeners;
+ }
+
+ /**
+ * Wraps the information about a given listener in a ListenerHook.ListenerInfo
+ * object.
+ * @param listeners The array of listeners.
+ * @param offset The offset into the array of the listener to wrap.
+ * @return A ListenerHook.ListenerInfo object for the specified listener.
+ */
+ private static ListenerHook.ListenerInfo wrapListener(Object[] listeners, int offset)
+ {
+ Filter filter = ((Filter)listeners[offset + LISTENER_FILTER_OFFSET]);
+
+ return new ListenerHookInfoImpl(
+ ((Bundle)listeners[offset + LISTENER_BUNDLE_OFFSET]).getBundleContext(),
+ filter == null ? null : filter.toString());
+ }
+
public void fireFrameworkEvent(FrameworkEvent event)
{
// Take a snapshot of the listener array.
diff --git a/framework/src/main/java/org/apache/felix/framework/util/ListenerHookInfoImpl.java b/framework/src/main/java/org/apache/felix/framework/util/ListenerHookInfoImpl.java
new file mode 100644
index 0000000..db36a8f
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/util/ListenerHookInfoImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.util;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.hooks.service.ListenerHook;
+
+public class ListenerHookInfoImpl implements ListenerHook.ListenerInfo
+{
+ private final BundleContext m_context;
+ private final String m_filter;
+
+ public ListenerHookInfoImpl(BundleContext context, String filter)
+ {
+ m_context = context;
+ m_filter = filter;
+ }
+
+ public BundleContext getBundleContext()
+ {
+ return m_context;
+ }
+
+ public String getFilter()
+ {
+ return m_filter;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/osgi/framework/ServiceException.java b/framework/src/main/java/org/osgi/framework/ServiceException.java
new file mode 100644
index 0000000..c67d3da
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/ServiceException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.osgi.framework;
+
+public class ServiceException extends RuntimeException
+{
+ private String type;
+
+ public ServiceException(String type)
+ {
+ this(type, null);
+ }
+
+ public ServiceException(String type, Throwable cause)
+ {
+ super(cause);
+ this.type = type;
+ }
+
+ public String getType()
+ {
+ return type;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/osgi/framework/hooks/service/ListenerHook.java b/framework/src/main/java/org/osgi/framework/hooks/service/ListenerHook.java
new file mode 100644
index 0000000..62e2a75
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/hooks/service/ListenerHook.java
@@ -0,0 +1,37 @@
+/*
+ * 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.osgi.framework.hooks.service;
+
+import java.util.Collection;
+
+import org.osgi.framework.BundleContext;
+
+public interface ListenerHook
+{
+ public static interface ListenerInfo
+ {
+ BundleContext getBundleContext();
+
+ String getFilter();
+ }
+
+ void added(Collection /* <? extends ListenerInfo> */ listeners);
+
+ void removed(Collection /* <? extends ListenerInfo> */ listener);
+}
\ No newline at end of file
diff --git a/framework/src/main/resources/default.properties b/framework/src/main/resources/default.properties
index 62ceb6a..71e01af 100644
--- a/framework/src/main/resources/default.properties
+++ b/framework/src/main/resources/default.properties
@@ -19,6 +19,7 @@
# Framework config properties.
#
org.osgi.framework.system.packages=org.osgi.framework; version=1.4.0, \
+ org.osgi.framework.hooks.service; version=1.4.0 \
org.osgi.service.packageadmin; version=1.2.0, \
org.osgi.service.startlevel; version=1.1.0, \
org.osgi.service.url; version=1.0.0, \
diff --git a/shell.tui/src/main/java/org/apache/felix/shell/tui/Activator.java b/shell.tui/src/main/java/org/apache/felix/shell/tui/Activator.java
index 9848d05..63014d5 100644
--- a/shell.tui/src/main/java/org/apache/felix/shell/tui/Activator.java
+++ b/shell.tui/src/main/java/org/apache/felix/shell/tui/Activator.java
@@ -110,17 +110,14 @@
if (m_runnable != null)
{
m_runnable.stop();
- m_thread.interrupt();
}
}
private class ShellTuiRunnable implements Runnable
{
- private volatile boolean stop = false;
-
public void stop()
{
- stop = true;
+ m_thread.interrupt();
}
public void run()
@@ -128,14 +125,24 @@
String line = null;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
- while (!stop)
+ while (true)
{
System.out.print("-> ");
try
{
+ while (System.in.available() == 0)
+ {
+ Thread.sleep(100);
+ }
+
line = in.readLine();
}
+ catch (InterruptedException ex)
+ {
+ // Silently exit, since this signifies that the bundle was stopped.
+ break;
+ }
catch (IOException ex)
{
System.err.println("ShellTUI: Error reading from stdin...exiting.");