org.amdatu.remote.AbstractEndpointPublishingComponent Maven / Gradle / Ivy
Show all versions of org.amdatu.remote.admin.http Show documentation
/*
* Licensed 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.amdatu.remote;
import static org.amdatu.remote.ServiceUtil.getStringPlusValue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.remoteserviceadmin.EndpointDescription;
import org.osgi.service.remoteserviceadmin.EndpointEvent;
import org.osgi.service.remoteserviceadmin.EndpointEventListener;
import org.osgi.service.remoteserviceadmin.EndpointListener;
/**
* Base class for service components that wish to inform listeners about {@link EndpointDescription} events based on
* their declared scope.
*
* This implementation tracks both {@code EndpointListener} and {@link EndpointEventListener} registrations, but will
* prefer the latter when a registration provides both.
*
* All internal state modifications and listeners calls are executed in-order through {@link #executeTask(Runnable)}.
*
* @author Amdatu Project Team
*/
@SuppressWarnings("deprecation")
public abstract class AbstractEndpointPublishingComponent extends AbstractComponent {
private final Map, AbstractListenerHandler>> m_listeners =
new HashMap, AbstractListenerHandler>>();
private final Set m_endpoints = new HashSet();
public AbstractEndpointPublishingComponent(String type, String name) {
super(type, name);
}
/**
* Component callback for Endpoint Event Listener addition.
*
* @param reference The Service Reference of the added Endpoint Event Listener
* @param listener The Endpoint Event Listener
*/
final void eventListenerAdded(final ServiceReference reference,
final EndpointEventListener listener) {
if (listener == this) {
logDebug("Ignoring Event Endpoint Listener because it is a reference this service instance: %s", reference);
return;
}
executeTask(new Runnable() {
@Override
public void run() {
logDebug("Adding Endpoint Event Listener %s", reference);
try {
EndpointEventListenerHandler handler =
new EndpointEventListenerHandler(reference, listener, m_endpoints);
AbstractListenerHandler> previous = m_listeners.put(reference, handler);
if (previous != null) {
logWarning("Endpoint Event Listener overwrites previous mapping %s", reference);
}
}
catch (Exception e) {
logError("Failed to handle added Endpoint Event Listener %s", e, reference);
}
}
});
}
/**
* Component callback for Endpoint Event Listener modification.
*
* @param reference The Service Reference of the added Endpoint Event Listener
* @param listener The Endpoint Event Listener
*/
final void eventListenerModified(final ServiceReference reference,
final EndpointEventListener listener) {
if (listener == this) {
logDebug("Ignoring Event Endpoint Listener because it is a reference this service instance: %s", reference);
return;
}
executeTask(new Runnable() {
@Override
public void run() {
logDebug("Modifying Endpoint Event Listener %s", listener);
try {
AbstractListenerHandler> handler = m_listeners.get(reference);
if (handler != null) {
handler.referenceModified(m_endpoints);
}
else {
logWarning("Failed to locate modified Endpoint Event Listener %s", reference);
}
}
catch (Exception e) {
logError("Failed to handle modified Endpoint Event Listener %s", e, reference);
}
}
});
}
/**
* Component callback for Endpoint Event Listener removal.
*
* @param reference The Service Reference of the added Endpoint Event Listener
* @param endpointListener The Endpoint Event Listener
*/
final void eventListenerRemoved(final ServiceReference reference,
final EndpointEventListener listener) {
if (listener == this) {
logDebug("Ignoring Event Endpoint Listener because it is a reference this service instance: %s", reference);
return;
}
executeTask(new Runnable() {
@Override
public void run() {
logDebug("Removing Endpoint Event Listener %s", reference);
AbstractListenerHandler> removed = m_listeners.remove(reference);
if (removed != null) {
removed.clearMatches();
}
else {
logWarning("Failed to locate removed Endpoint Event Listener %s", reference);
}
}
});
}
/**
* Component callback for Endpoint Listener addition.
*
* @param reference The Service Reference of the added Endpoint Listener
* @param listener The Endpoint Listener
*/
final void listenerAdded(final ServiceReference reference, final EndpointListener listener) {
if (listener == this) {
logDebug("Ignoring Endpoint Listener because it is a reference this service instance: %s", reference);
return;
}
if (isEndpointEventListener(reference)) {
logDebug("Ignoring Endpoint Listener because it is registered as an Endpoint Event Listener as well: %s",
reference);
return;
}
executeTask(new Runnable() {
@Override
public void run() {
logDebug("Adding Endpoint Listener %s", reference);
try {
EndpointListenerHandler holder = new EndpointListenerHandler(reference, listener, m_endpoints);
AbstractListenerHandler> previous = m_listeners.put(reference, holder);
if (previous != null) {
logWarning("Endpoint Listener overwrites previous mapping %s", reference);
}
}
catch (Exception e) {
logError("Failed to handle added Endpoint Listener %s", e, reference);
}
}
});
}
/**
* Component callback for endpointListener modification.
*
* @param reference The serviceReference of the added endpointListener
* @param listener The endpointListener
*/
final void listenerModified(final ServiceReference reference, final EndpointListener listener) {
if (listener == this) {
logDebug("Ignoring Endpoint Listener because it is a reference this service instance: %s", reference);
return;
}
if (isEndpointEventListener(reference)) {
logDebug("Ignoring Endpoint Listener because it is registered as an Endpoint Event Listener as well: %s",
reference);
return;
}
executeTask(new Runnable() {
@Override
public void run() {
logDebug("Modifying Endpoint Listener %s", listener);
try {
AbstractListenerHandler> handler = m_listeners.get(reference);
if (handler != null) {
handler.referenceModified(m_endpoints);
}
else {
logWarning("Failed to locate modified Endpoint Listener %s", reference);
}
}
catch (Exception e) {
logError("Failed to handle modified Endpoint Listener %s", e, reference);
}
}
});
}
/**
* Component callback for Endpoint Listener removal.
*
* @param serviceReference The Service Reference of the added Endpoint Listener
* @param listener The Endpoint Listener
*/
final void listenerRemoved(final ServiceReference reference, final EndpointListener listener) {
if (listener == this) {
logDebug("Ignoring Endpoint Listener because it is a reference this service instance: %s", reference);
return;
}
if (isEndpointEventListener(reference)) {
logDebug("Ignoring Endpoint Listener because it is registered as an Endpoint Event Listener as well: %s",
reference);
return;
}
executeTask(new Runnable() {
@Override
public void run() {
logDebug("Removing Endpoint Listener %s", reference);
AbstractListenerHandler> removed = m_listeners.remove(reference);
if (removed != null) {
removed.clearMatches();
}
else {
logWarning("Failed to locate removed Endpoint Listener %s", reference);
}
}
});
}
/**
* Call Endpoint added on all registered listeners with as scope that matches the specified endpointDescription.
*
* @param description The Endpoint Description
* @throws IllegalStateException if called with a previsouly added Endpoint Description
*/
protected final void endpointAdded(final EndpointDescription description) {
executeTask(new Runnable() {
@Override
public void run() {
logDebug("Adding Endpoint: %s", description);
if (!m_endpoints.add(description)) {
throw new IllegalStateException("Trying to add duplicate Endpoint Description: " + description);
}
for (AbstractListenerHandler> handler : m_listeners.values()) {
try {
handler.endpointAdded(description);
}
catch (Exception e) {
logWarning("Caught exception while invoking Endpoint added on %s", e, handler.getReference());
}
}
}
});
}
/**
* Call Endpoint removed on all registered listeners with a scope that matches the specified endpointDescription.
*
* @param endpoint The Endpoint Description
* @throws IllegalStateException if called with an unknown Endpoint Description
*/
protected final void endpointRemoved(final EndpointDescription endpoint) {
executeTask(new Runnable() {
@Override
public void run() {
logDebug("Removing Endpoint: %s", endpoint);
if (!m_endpoints.remove(endpoint)) {
throw new IllegalStateException("Trying to remove unknown Endpoint Description: " + endpoint);
}
for (AbstractListenerHandler> handler : m_listeners.values()) {
try {
handler.endpointRemoved(endpoint);
}
catch (Exception e) {
logWarning("Caught exception while invoking Endpoint removed on %s", e, handler.getReference());
}
}
}
});
}
/**
* Call Endpoint modified on all registered listeners with as scope that matches the specified endpointDescription.
*
* @param description The Endpoint Description
* @throws IllegalStateException if called with an unknown Endpoint Description
*/
protected final void endpointModified(final EndpointDescription endpoint) {
executeTask(new Runnable() {
@Override
public void run() {
logDebug("Modifying Endpoint: %s", endpoint);
if (!m_endpoints.remove(endpoint)) {
throw new IllegalStateException("Trying to modify unknown Endpoint Description: " + endpoint);
}
m_endpoints.add(endpoint);
for (AbstractListenerHandler> handler : m_listeners.values()) {
try {
handler.endpointModified(endpoint);
}
catch (Exception e) {
logWarning("Caught exception while invoking Endpoint removed on %s", e, handler.getReference());
}
}
}
});
}
/**
* Determine whether a service reference exposes a services that implements EndpointEventListener.
*
* @param reference the Service Reference
* @return {@code true} is the objectclass includes EndpointEventListener, otherwise {@code false}
*/
private static boolean isEndpointEventListener(ServiceReference> reference) {
String[] objectClass = getStringPlusValue(reference.getProperty(Constants.OBJECTCLASS));
for (int i = 0; i < objectClass.length; i++) {
if (objectClass[i].equals(EndpointEventListener.class.getName())) {
return true;
}
}
return false;
}
/**
* Abstract handler for listeners that encapsulates filter parsing, caching and matching
*
* This implementation is not thread-safe. Synchronization is handled from the outside.
*
* @param The concrete listener type
*/
private static abstract class AbstractListenerHandler {
private final ServiceReference m_reference;
private final String m_scopeKey;
private final T m_listener;
private final List m_filters = new ArrayList();
private final Map m_matches = new HashMap();
/**
* Constructs a new handler and initializes by calling {@link #referenceModified(Collection)} internally.
*
* @param reference The listener Service Reference
* @param listener The listener of type T
* @param scopeKey The scope property key
* @param endpoints The current Endpoint collection
* @throws Exception If the initialization fails
*/
public AbstractListenerHandler(ServiceReference reference, T listener, String scopeKey,
Collection endpoints) throws Exception {
m_reference = reference;
m_listener = listener;
m_scopeKey = scopeKey;
referenceModified(endpoints);
}
/**
* Updates the handler state and invokes the relevant listener callbacks according to the specified
* collection of published Endpoint Descriptions.
*
* @param endpoints The current collection of Endpoint Descriptions
* @throws Exception If the update fails
*/
public final void referenceModified(Collection endpoints) throws Exception {
updateFilters();
updateMatches(endpoints);
}
/**
* Returns the listener.
*
* @return The listener
*/
public final T getListener() {
return m_listener;
}
/**
* Return the reference.
*
* @return The reference
*/
public final ServiceReference getReference() {
return m_reference;
}
/**
* Return the first matching filter for the specified Endpoint Description.
*
* @param endpoint The Endpoint Description
* @return The first matching Filter, or {@code null}
*/
private final Filter getFirstMatchingFilter(EndpointDescription endpoint) {
for (Filter filter : m_filters) {
if (filter.matches(endpoint.getProperties())) {
return filter;
}
}
return null;
}
/**
* Invoke the added callback on the listener if the scope matches the specified Endpoint Description.
*
* @param endpoint The Endpoint Description
*/
public final void endpointAdded(EndpointDescription endpoint) {
Filter filter = getFirstMatchingFilter(endpoint);
if (filter != null) {
m_matches.put(endpoint, filter);
endpointAdded(endpoint, filter);
}
}
/**
* Invoke the removed callback on the listener if the scope matches the specified Endpoint Description.
*
* @param endpoint The Endpoint Description
*/
public final void endpointRemoved(EndpointDescription endpoint) {
Filter filter = m_matches.remove(endpoint);
if (filter != null) {
endpointRemoved(endpoint, filter);
}
}
/**
* Invoke the relevant callback on the listener if the scope matches the specified Endpoint Description.
*
* @param endpoint The Endpoint Description
*/
public final void endpointModified(EndpointDescription endpoint) {
Filter matchedFilter = getFirstMatchingFilter(endpoint);
Filter previouslyMatchedFilter = m_matches.remove(endpoint);
if (matchedFilter != null) {
m_matches.put(endpoint, matchedFilter);
if (previouslyMatchedFilter != null) {
endpointModified(endpoint, matchedFilter);
}
else {
endpointAdded(endpoint, matchedFilter);
}
}
else if (previouslyMatchedFilter != null) {
endpointEndmatch(endpoint, matchedFilter);
}
}
/**
* Invoke the relevant callback on the listener.
*
* @param endpoint The Endpoint Description
* @param matchedFilter The matched Filter
*/
protected abstract void endpointAdded(EndpointDescription endpoint, Filter matchedFilter);
/**
* Invoke the relevant callback on the listener.
*
* @param endpoint The Endpoint Description
* @param matchedFilter The matched Filter
*/
protected abstract void endpointRemoved(EndpointDescription endpoint, Filter matchedFilter);
/**
* Invoke the relevant callback on the listener.
*
* @param endpoint The Endpoint Description
* @param matchedFilter The matched Filter
*/
protected abstract void endpointModified(EndpointDescription endpoint, Filter matchedFilter);
/**
* Invoke the relevant callback on the listener.
*
* @param endpoint The Endpoint Description
* @param matchedFilter The matched Filter
*/
protected abstract void endpointEndmatch(EndpointDescription endpoint, Filter matchedFilter);
private final void updateFilters() throws InvalidSyntaxException {
m_filters.clear();
Object value = m_reference.getProperty(m_scopeKey);
if (value == null) {
return;
}
String[] scopes = getStringPlusValue(value);
if (scopes == null || scopes.length == 0) {
return;
}
List filters = new ArrayList();
for (String scope : scopes) {
if (!scope.trim().equals("")) {
filters.add(FrameworkUtil.createFilter(scope));
}
}
m_filters.addAll(filters);
}
private final void updateMatches(Collection endpoints) {
for (EndpointDescription endpoint : endpoints) {
Filter matchedFilter = getFirstMatchingFilter(endpoint);
if (matchedFilter != null) {
if (!m_matches.containsKey(endpoint)) {
m_matches.put(endpoint, matchedFilter);
endpointAdded(endpoint, matchedFilter);
}
else if (!matchedFilter.equals(m_matches.get(endpoint))) {
m_matches.put(endpoint, matchedFilter);
endpointModified(endpoint, matchedFilter);
}
else {
// nothing changed
}
}
else {
Filter previouslyMatchedFilter = m_matches.remove(endpoint);
if (previouslyMatchedFilter != null) {
endpointEndmatch(endpoint, previouslyMatchedFilter);
}
}
}
}
private final void clearMatches() {
for (Entry entry : m_matches.entrySet()) {
endpointRemoved(entry.getKey(), entry.getValue());
}
m_matches.clear();
}
}
/**
* Concrete holder for type Endpoint Event Listener.
*/
private static class EndpointEventListenerHandler extends AbstractListenerHandler {
public EndpointEventListenerHandler(ServiceReference reference,
EndpointEventListener listener, Collection endpoints) throws Exception {
super(reference, listener, EndpointEventListener.ENDPOINT_LISTENER_SCOPE, endpoints);
}
@Override
protected void endpointAdded(EndpointDescription description, Filter matchedFilter) {
try {
getListener().endpointChanged(new EndpointEvent(EndpointEvent.ADDED, description),
matchedFilter.toString());
}
catch (Exception e) {}
}
@Override
protected void endpointRemoved(EndpointDescription description, Filter matchedFilter) {
try {
getListener().endpointChanged(new EndpointEvent(EndpointEvent.REMOVED, description),
matchedFilter.toString());
}
catch (Exception e) {}
}
@Override
protected void endpointModified(EndpointDescription description, Filter matchedFilter) {
try {
getListener().endpointChanged(new EndpointEvent(EndpointEvent.MODIFIED, description),
matchedFilter.toString());
}
catch (Exception e) {}
}
@Override
protected void endpointEndmatch(EndpointDescription description, Filter matchedFilter) {
try {
getListener().endpointChanged(new EndpointEvent(EndpointEvent.MODIFIED_ENDMATCH, description),
matchedFilter.toString());
}
catch (Exception e) {}
}
}
/**
* Concrete holder for deprecated type Endpoint Listener.
*/
private static class EndpointListenerHandler extends AbstractListenerHandler {
public EndpointListenerHandler(ServiceReference reference,
EndpointListener listener, Collection endpoints) throws Exception {
super(reference, listener, EndpointListener.ENDPOINT_LISTENER_SCOPE, endpoints);
}
@Override
protected void endpointAdded(EndpointDescription description, Filter matchedFilter) {
try {
getListener().endpointAdded(description, matchedFilter.toString());
}
catch (Exception e) {}
}
@Override
protected void endpointRemoved(EndpointDescription description, Filter matchedFilter) {
try {
getListener().endpointRemoved(description, matchedFilter.toString());
}
catch (Exception e) {}
}
@Override
protected void endpointModified(EndpointDescription description, Filter matchedFilter) {
try {
getListener().endpointRemoved(description, matchedFilter.toString());
getListener().endpointAdded(description, matchedFilter.toString());
}
catch (Exception e) {}
}
@Override
protected void endpointEndmatch(EndpointDescription description, Filter matchedFilter) {
try {
getListener().endpointRemoved(description, matchedFilter.toString());
}
catch (Exception e) {}
}
}
}