Skip to content

Commit

Permalink
Merge pull request #8 from TheItivitist/ipojo-1.12.1-ullink
Browse files Browse the repository at this point in the history
Improve internal event dispatcher
  • Loading branch information
benoitdesire authored Aug 1, 2022
2 parents 61bc348 + 3c2a0af commit 4d3299d
Show file tree
Hide file tree
Showing 9 changed files with 814 additions and 86 deletions.
10 changes: 8 additions & 2 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<packaging>bundle</packaging>
<name>Apache Felix iPOJO</name>
<artifactId>org.apache.felix.ipojo</artifactId>
<version>1.12.1.4-ullink-SNAPSHOT</version>
<version>1.12.1.5-ullink-SNAPSHOT</version>

<properties>
<!--
Expand Down Expand Up @@ -109,7 +109,13 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
Expand Down
249 changes: 211 additions & 38 deletions core/src/main/java/org/apache/felix/ipojo/EventDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,23 @@
package org.apache.felix.ipojo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.felix.ipojo.extender.ExtensionDeclaration;
import org.apache.felix.ipojo.extender.InstanceDeclaration;
import org.osgi.framework.AllServiceListener;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;

/**
* iPOJO Internal event dispatcher.
Expand All @@ -37,24 +44,154 @@
* @author <a href="mailto:[email protected]">Felix Project Team</a>
* @see org.osgi.framework.ServiceListener
*/
public final class EventDispatcher implements ServiceListener {

public final class EventDispatcher implements AllServiceListener
{
static class ListenerKey
{
private static final Pattern OBJECT_CLASS_FILTER = Pattern.compile("\\(object[cC]lass=(\\S+)\\)");
private static final Pattern HANDLER_TYPE_FILTER = Pattern.compile("\\(&\\(handler\\.type=primitive\\)\\(factory\\.state=1\\)\\)");
private static final Pattern EXTENSION_DECLARATION_FILTER = Pattern.compile("\\(&\\(object[cC]lass=org\\.apache\\.felix\\.ipojo\\.extender\\.ExtensionDeclaration\\)\\(ipojo\\.extension\\.name=(\\S+)\\)\\)");
private static final Pattern INSTANCE_DECLARATION_FILTER = Pattern.compile("\\(&\\(object[cC]lass=org\\.apache\\.felix\\.ipojo\\.extender\\.InstanceDeclaration\\)\\(\\|\\(ipojo\\.component\\.name=(\\S+)\\)\\(ipojo\\.component\\.name=(\\S+)\\)\\)\\(!\\(ipojo\\.component\\.version=\\*\\)\\)\\)");
private static final String EXTENSION_DECLARATION_CLASSNAME = "org.apache.felix.ipojo.extender.ExtensionDeclaration";
private static final String INSTANCE_DECLARATION_CLASSNAME = "org.apache.felix.ipojo.extender.InstanceDeclaration";
private static final String FACTORY_STATE = "factory.state";

static List<ListenerKey> build(String filter) {
if (filter == null || filter.isEmpty()) {
return null;
}
Matcher objectClassMatcher = OBJECT_CLASS_FILTER.matcher(filter);
if (objectClassMatcher.matches()) {
String objectClass = objectClassMatcher.group(1);
return Collections.singletonList(new ListenerKey(objectClass, null, null, null, null, null));
}
Matcher handlerTypeMatcher = HANDLER_TYPE_FILTER.matcher(filter);
if (handlerTypeMatcher.matches()) {
return Collections.singletonList(new ListenerKey(null, null, null, null, PrimitiveHandler.HANDLER_TYPE, Factory.VALID));
}
Matcher extensionDeclarationMatcher = EXTENSION_DECLARATION_FILTER.matcher(filter);
if (extensionDeclarationMatcher.matches()) {
String extensionName = extensionDeclarationMatcher.group(1);
return Collections.singletonList(new ListenerKey(EXTENSION_DECLARATION_CLASSNAME, extensionName, null, null, null, null));
}
Matcher instanceDeclarationMatcher = INSTANCE_DECLARATION_FILTER.matcher(filter);
if (instanceDeclarationMatcher.matches()) {
String componentName1 = instanceDeclarationMatcher.group(1);
String componentName2 = instanceDeclarationMatcher.group(2);
return Arrays.asList(
new ListenerKey(INSTANCE_DECLARATION_CLASSNAME, null, componentName1, false, null, null),
new ListenerKey(INSTANCE_DECLARATION_CLASSNAME, null, componentName2, false, null, null)
);
}
return null;
}

static List<ListenerKey> build(ServiceReference<?> serviceReference) {
String[] objectClasses = (String[]) serviceReference.getProperty(Constants.OBJECTCLASS);
String handlerType = (String) serviceReference.getProperty(Handler.HANDLER_TYPE_PROPERTY);
List<ListenerKey> results = new ArrayList<ListenerKey>(objectClasses.length + (handlerType != null ? 1 : 0));
if (PrimitiveHandler.HANDLER_TYPE.equals(handlerType)) {
Integer factoryState = (Integer) serviceReference.getProperty(FACTORY_STATE);
if (factoryState != null && factoryState == Factory.VALID) {
results.add(new ListenerKey(null, null, null, null, handlerType, factoryState));
}
}
for (String objectClass : objectClasses) {
if (EXTENSION_DECLARATION_CLASSNAME.equals(objectClass)) {
String extensionName = (String) serviceReference.getProperty(ExtensionDeclaration.EXTENSION_NAME_PROPERTY);
results.add(new ListenerKey(objectClass, extensionName, null, null, null, null));
} else if (INSTANCE_DECLARATION_CLASSNAME.equals(objectClass)) {
String componentName = (String) serviceReference.getProperty(InstanceDeclaration.COMPONENT_NAME_PROPERTY);
String componentVersion = (String) serviceReference.getProperty(InstanceDeclaration.COMPONENT_VERSION_PROPERTY);
results.add(new ListenerKey(objectClass, null, componentName, componentVersion != null && !componentVersion.isEmpty(), null, null));
} else {
results.add(new ListenerKey(objectClass, null, null, null, null, null));
}
}
return results;
}

final String objectClass;
final String extensionName;
final String componentName;
final Boolean componentVersion;
final String handleType;
final Integer factoryState;

ListenerKey(String objectClass, String extensionName, String componentName, Boolean componentVersion, String handleType, Integer factoryState) {
this.objectClass = objectClass;
this.extensionName = extensionName;
this.componentName = componentName;
this.componentVersion = componentVersion;
this.handleType = handleType;
this.factoryState = factoryState;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof ListenerKey) {
ListenerKey listenerKey = (ListenerKey) o;
return objectClass != null ? objectClass.equals(listenerKey.objectClass) : listenerKey.objectClass == null
&& extensionName != null ? extensionName.equals(listenerKey.extensionName) : listenerKey.extensionName == null
&& componentName != null ? componentName.equals(listenerKey.componentName) : listenerKey.componentName == null
&& (componentVersion == null || componentVersion.equals(listenerKey.componentVersion))
&& handleType != null ? handleType.equals(listenerKey.handleType) : listenerKey.handleType == null
&& factoryState != null ? factoryState.equals(listenerKey.factoryState) : listenerKey.factoryState == null;
}
return false;
}

@Override
public int hashCode() {
int result = objectClass != null ? objectClass.hashCode() : 0;
result = 31 * result + (extensionName != null ? extensionName.hashCode() : 0);
result = 31 * result + (componentName != null ? componentName.hashCode() : 0);
result = 31 * result + (componentVersion != null ? componentVersion.hashCode() : 0);
result = 31 * result + (handleType != null ? handleType.hashCode() : 0);
result = 31 * result + (factoryState != null ? factoryState.hashCode() : 0);
return result;
}
}

private static class ListenerInfo {
private final Bundle bundle;
private final ServiceListener listener;

ListenerInfo(Bundle bundle, ServiceListener listener) {
this.bundle = bundle;
this.listener = listener;
}

Bundle getBundle() {
return bundle;
}

ServiceListener getListener() {
return listener;
}
}

/**
* The internal event dispatcher.
* This dispatcher is a singleton.
*/
private static EventDispatcher DISPATCHER;


private static final ListenerInfo[] NO_LISTENERINFO = new ListenerInfo[0];

/**
* The list of listeners.
* Service interface -> List of {@link ServiceListener}
*/
private Map m_listeners;
private final Map<ListenerKey, List<ListenerInfo>> m_listeners;
/**
* The global bundle context.
* This is the bundle context from iPOJO.
*/
private BundleContext m_context;
private final BundleContext m_context;

/**
* Creates the EventDispatcher.
Expand All @@ -63,7 +200,7 @@ public final class EventDispatcher implements ServiceListener {
*/
private EventDispatcher(BundleContext bc) {
m_context = bc;
m_listeners = new HashMap();
m_listeners = new HashMap<ListenerKey, List<ListenerInfo>>();
// Only one thread can call the start method.
m_context.addServiceListener(this);
}
Expand Down Expand Up @@ -122,60 +259,96 @@ private void stop() {
* @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
*/
public void serviceChanged(ServiceEvent event) {
String[] itfs = (String[]) event.getServiceReference().getProperty(Constants.OBJECTCLASS);
for (int s = 0; s < itfs.length; s++) {
List list;
List<ListenerKey> listenerKeys = ListenerKey.build(event.getServiceReference());
for(ListenerKey listenerKey : listenerKeys) {
ListenerInfo[] listenerInfos;
synchronized (this) {
List stored = (List) m_listeners.get(itfs[s]);
if (stored == null) {
return; // Nothing to do
}
// Creates a new list (stack confinement)
list = new ArrayList(stored);
List<ListenerInfo> list = m_listeners.get(listenerKey);
listenerInfos = list == null || list.isEmpty()
? null
: list.toArray(NO_LISTENERINFO);
}
for (int i = 0; i < list.size(); i++) {
((ServiceListener) list.get(i)).serviceChanged(event);
fireServiceEvents(listenerKey.objectClass, listenerInfos, event);
}
}

private void fireServiceEvents(String specification, ListenerInfo[] listenerInfos, ServiceEvent event) {
if (listenerInfos != null) {
for (ListenerInfo listenerInfo : listenerInfos) {
fireServiceEvent(specification, listenerInfo.getListener(), listenerInfo.getBundle(), event);
}
}
}


private void fireServiceEvent(String specification, ServiceListener listener, Bundle bundle, ServiceEvent event) {
if (canFireServiceEvent(specification, listener, bundle, event)) {
listener.serviceChanged(event);
}
}

private boolean canFireServiceEvent(String specification, ServiceListener listener, Bundle bundle, ServiceEvent event) {
if (bundle == m_context.getBundle()) {
return true;
}
int state = bundle.getState();
if (state != Bundle.ACTIVE && state != Bundle.STARTING && state != Bundle.STOPPING) {
return false;
}
return listener instanceof AllServiceListener
|| specification == null
|| event.getServiceReference().isAssignableTo(bundle, specification);
}

/**
* Adds a new service listener to the {@link EventDispatcher#m_listeners}
* map. This method specifies the listen service interface
* @param itf the service interface
* @param filter the filter
* @param bundle the bundle that is registering the listener
* @param listener the service listener
*/
public void addListener(String itf, ServiceListener listener) {
public boolean addListener(String filter, Bundle bundle, ServiceListener listener) {
List<ListenerKey> listenerKeys = ListenerKey.build(filter);
if (listenerKeys == null) {
return false;
}
ListenerInfo listenerInfo = new ListenerInfo(bundle, listener);
synchronized (this) {
List list = (List) m_listeners.get(itf);
if (list == null) {
list = new ArrayList(1);
list.add(listener);
m_listeners.put(itf, list);
} else {
list.add(listener);
for (ListenerKey listenerKey : listenerKeys) {
List<ListenerInfo> list = m_listeners.get(listenerKey);
if (list == null) {
list = new ArrayList(1);
m_listeners.put(listenerKey, list);
}
list.add(listenerInfo);
}
}
return true;
}

/**
* Removes a service listener.
* @param listener the service listener to remove
* @return <code>true</code> if the listener is
* successfully removed.
*/
public boolean removeListener(ServiceListener listener) {
boolean removed = false;
synchronized (this) {
Set keys = m_listeners.keySet();
Iterator it = keys.iterator();
while (it.hasNext()) {
String itf = (String) it.next();
List list = (List) m_listeners.get(itf);
removed = removed || list.remove(listener);
boolean removed = false;
for (List<ListenerInfo> listenerInfoList : m_listeners.values()) {
removed = removeListener(listenerInfoList, listener) || removed;
}
return removed;
}
return removed;
}

private boolean removeListener(List<ListenerInfo> listenerInfoList, ServiceListener listener) {
Iterator<ListenerInfo> iterator = listenerInfoList.iterator();
while (iterator.hasNext()) {
if (iterator.next().getListener() == listener) {
iterator.remove();
return true;
}
}
return false;
}
}
Loading

0 comments on commit 4d3299d

Please sign in to comment.