blob: 546ee5d1a732525484a274ae5fc49cba6a5bba68 [file] [log] [blame]
/**
*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.
*/
/*
* service_tracker.c
*
* \date Apr 20, 2010
* \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
* \copyright Apache License, Version 2.0
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <service_reference_private.h>
#include <framework_private.h>
#include <assert.h>
#include "service_tracker_private.h"
#include "bundle_context.h"
#include "constants.h"
#include "service_reference.h"
#include "celix_log.h"
static celix_status_t serviceTracker_invokeAddingService(service_tracker_pt tracker, service_reference_pt reference,
void **service);
static celix_status_t serviceTracker_track(service_tracker_pt tracker, service_reference_pt reference, service_event_pt event);
static celix_status_t serviceTracker_untrack(service_tracker_pt tracker, service_reference_pt reference, service_event_pt event);
static celix_status_t serviceTracker_invokeAddService(service_tracker_pt tracker, service_reference_pt ref, void *service);
static celix_status_t serviceTracker_invokeModifiedService(service_tracker_pt tracker, service_reference_pt ref, void *service);
static celix_status_t serviceTracker_invokeRemovingService(service_tracker_pt tracker, service_reference_pt ref,
void *service);
celix_status_t serviceTracker_create(bundle_context_pt context, char * service, service_tracker_customizer_pt customizer, service_tracker_pt *tracker) {
celix_status_t status = CELIX_SUCCESS;
if (service == NULL || *tracker != NULL) {
status = CELIX_ILLEGAL_ARGUMENT;
} else {
if (status == CELIX_SUCCESS) {
char filter[512];
snprintf(filter, sizeof(filter), "(%s=%s)", OSGI_FRAMEWORK_OBJECTCLASS, service);
serviceTracker_createWithFilter(context, filter, customizer, tracker);
}
}
framework_logIfError(logger, status, NULL, "Cannot create service tracker");
return status;
}
celix_status_t serviceTracker_createWithFilter(bundle_context_pt context, char * filter, service_tracker_customizer_pt customizer, service_tracker_pt *tracker) {
celix_status_t status = CELIX_SUCCESS;
*tracker = (service_tracker_pt) malloc(sizeof(**tracker));
if (!*tracker) {
status = CELIX_ENOMEM;
} else {
(*tracker)->context = context;
(*tracker)->filter = strdup(filter);
(*tracker)->tracker = *tracker;
celixThreadRwlock_create(&(*tracker)->lock, NULL);
(*tracker)->trackedServices = NULL;
arrayList_create(&(*tracker)->trackedServices);
(*tracker)->customizer = customizer;
(*tracker)->listener = NULL;
}
framework_logIfError(logger, status, NULL, "Cannot create service tracker [filter=%s]", filter);
return status;
}
celix_status_t serviceTracker_destroy(service_tracker_pt tracker) {
if (tracker->listener != NULL) {
bundleContext_removeServiceListener(tracker->context, tracker->listener);
}
if (tracker->customizer != NULL) {
serviceTrackerCustomizer_destroy(tracker->customizer);
}
celixThreadRwlock_writeLock(&tracker->lock);
arrayList_destroy(tracker->trackedServices);
celixThreadRwlock_unlock(&tracker->lock);
if (tracker->listener != NULL) {
free (tracker->listener);
}
celixThreadRwlock_destroy(&tracker->lock);
free(tracker->filter);
free(tracker);
return CELIX_SUCCESS;
}
celix_status_t serviceTracker_open(service_tracker_pt tracker) {
service_listener_pt listener;
array_list_pt initial = NULL;
celix_status_t status = CELIX_SUCCESS;
listener = (service_listener_pt) malloc(sizeof(*listener));
status = bundleContext_getServiceReferences(tracker->context, NULL, tracker->filter, &initial);
if (status == CELIX_SUCCESS) {
service_reference_pt initial_reference;
unsigned int i;
listener->handle = tracker;
listener->serviceChanged = (void *) serviceTracker_serviceChanged;
status = bundleContext_addServiceListener(tracker->context, listener, tracker->filter);
if (status == CELIX_SUCCESS) {
tracker->listener = listener;
for (i = 0; i < arrayList_size(initial); i++) {
initial_reference = (service_reference_pt) arrayList_get(initial, i);
serviceTracker_track(tracker, initial_reference, NULL);
}
arrayList_clear(initial);
arrayList_destroy(initial);
initial = NULL;
}
}
framework_logIfError(logger, status, NULL, "Cannot open tracker");
return status;
}
celix_status_t serviceTracker_close(service_tracker_pt tracker) {
celix_status_t status = CELIX_SUCCESS;
if (status == CELIX_SUCCESS) {
array_list_pt refs = serviceTracker_getServiceReferences(tracker);
if (refs != NULL) {
unsigned int i;
for (i = 0; i < arrayList_size(refs); i++) {
service_reference_pt ref = (service_reference_pt) arrayList_get(refs, i);
status = serviceTracker_untrack(tracker, ref, NULL);
}
}
arrayList_destroy(refs);
}
if (status == CELIX_SUCCESS) {
status = bundleContext_removeServiceListener(tracker->context, tracker->listener);
}
framework_logIfError(logger, status, NULL, "Cannot close tracker");
return status;
}
service_reference_pt serviceTracker_getServiceReference(service_tracker_pt tracker) {
tracked_pt tracked;
service_reference_pt result = NULL;
unsigned int i;
celixThreadRwlock_readLock(&tracker->lock);
for (i = 0; i < arrayList_size(tracker->trackedServices); i++) {
tracked = (tracked_pt) arrayList_get(tracker->trackedServices, i);
result = tracked->reference;
break;
}
celixThreadRwlock_unlock(&tracker->lock);
return result;
}
array_list_pt serviceTracker_getServiceReferences(service_tracker_pt tracker) {
tracked_pt tracked;
unsigned int i;
array_list_pt references = NULL;
arrayList_create(&references);
celixThreadRwlock_readLock(&tracker->lock);
for (i = 0; i < arrayList_size(tracker->trackedServices); i++) {
tracked = (tracked_pt) arrayList_get(tracker->trackedServices, i);
arrayList_add(references, tracked->reference);
}
celixThreadRwlock_unlock(&tracker->lock);
return references;
}
void *serviceTracker_getService(service_tracker_pt tracker) {
tracked_pt tracked;
void *service = NULL;
unsigned int i;
celixThreadRwlock_readLock(&tracker->lock);
for (i = 0; i < arrayList_size(tracker->trackedServices); i++) {
tracked = (tracked_pt) arrayList_get(tracker->trackedServices, i);
service = tracked->service;
break;
}
celixThreadRwlock_unlock(&tracker->lock);
return service;
}
array_list_pt serviceTracker_getServices(service_tracker_pt tracker) {
tracked_pt tracked;
unsigned int i;
array_list_pt references = NULL;
arrayList_create(&references);
celixThreadRwlock_readLock(&tracker->lock);
for (i = 0; i < arrayList_size(tracker->trackedServices); i++) {
tracked = (tracked_pt) arrayList_get(tracker->trackedServices, i);
arrayList_add(references, tracked->service);
}
celixThreadRwlock_unlock(&tracker->lock);
return references;
}
void *serviceTracker_getServiceByReference(service_tracker_pt tracker, service_reference_pt reference) {
tracked_pt tracked;
void *service = NULL;
unsigned int i;
celixThreadRwlock_readLock(&tracker->lock);
for (i = 0; i < arrayList_size(tracker->trackedServices); i++) {
bool equals = false;
tracked = (tracked_pt) arrayList_get(tracker->trackedServices, i);
serviceReference_equals(reference, tracked->reference, &equals);
if (equals) {
service = tracked->service;
break;
}
}
celixThreadRwlock_unlock(&tracker->lock);
return service;
}
void serviceTracker_serviceChanged(service_listener_pt listener, service_event_pt event) {
service_tracker_pt tracker = listener->handle;
switch (event->type) {
case OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED:
serviceTracker_track(tracker, event->reference, event);
break;
case OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED:
serviceTracker_track(tracker, event->reference, event);
break;
case OSGI_FRAMEWORK_SERVICE_EVENT_UNREGISTERING:
serviceTracker_untrack(tracker, event->reference, event);
break;
case OSGI_FRAMEWORK_SERVICE_EVENT_MODIFIED_ENDMATCH:
//TODO
break;
}
}
static celix_status_t serviceTracker_track(service_tracker_pt tracker, service_reference_pt reference, service_event_pt event) {
celix_status_t status = CELIX_SUCCESS;
tracked_pt tracked = NULL;
bool found = false;
unsigned int i;
bundleContext_retainServiceReference(tracker->context, reference);
celixThreadRwlock_readLock(&tracker->lock);
for (i = 0; i < arrayList_size(tracker->trackedServices); i++) {
bool equals = false;
tracked = (tracked_pt) arrayList_get(tracker->trackedServices, i);
status = serviceReference_equals(reference, tracked->reference, &equals);
if (status != CELIX_SUCCESS) {
break;
}
if (equals) {
found = true;
break;
}
}
celixThreadRwlock_unlock(&tracker->lock);
if (status == CELIX_SUCCESS && !found /*new*/) {
void * service = NULL;
status = serviceTracker_invokeAddingService(tracker, reference, &service);
if (status == CELIX_SUCCESS) {
if (service != NULL) {
tracked = (tracked_pt) calloc(1, sizeof (*tracked));
assert(reference != NULL);
tracked->reference = reference;
tracked->service = service;
celixThreadRwlock_writeLock(&tracker->lock);
arrayList_add(tracker->trackedServices, tracked);
celixThreadRwlock_unlock(&tracker->lock);
serviceTracker_invokeAddService(tracker, reference, service);
}
}
} else {
status = serviceTracker_invokeModifiedService(tracker, reference, tracked->service);
}
framework_logIfError(logger, status, NULL, "Cannot track reference");
return status;
}
static celix_status_t serviceTracker_invokeModifiedService(service_tracker_pt tracker, service_reference_pt ref, void *service) {
celix_status_t status = CELIX_SUCCESS;
if (tracker->customizer != NULL) {
void *handle = NULL;
modified_callback_pt function = NULL;
serviceTrackerCustomizer_getHandle(tracker->customizer, &handle);
serviceTrackerCustomizer_getModifiedFunction(tracker->customizer, &function);
if (function != NULL) {
function(handle, ref, service);
}
}
return status;
}
static celix_status_t serviceTracker_invokeAddService(service_tracker_pt tracker, service_reference_pt ref, void *service) {
celix_status_t status = CELIX_SUCCESS;
if (tracker->customizer != NULL) {
void *handle = NULL;
added_callback_pt function = NULL;
serviceTrackerCustomizer_getHandle(tracker->customizer, &handle);
serviceTrackerCustomizer_getAddedFunction(tracker->customizer, &function);
if (function != NULL) {
function(handle, ref, service);
}
}
return status;
}
static celix_status_t serviceTracker_invokeAddingService(service_tracker_pt tracker, service_reference_pt reference,
void **service) {
celix_status_t status = CELIX_SUCCESS;
if (tracker->customizer != NULL) {
void *handle = NULL;
adding_callback_pt function = NULL;
status = serviceTrackerCustomizer_getHandle(tracker->customizer, &handle);
if (status == CELIX_SUCCESS) {
status = serviceTrackerCustomizer_getAddingFunction(tracker->customizer, &function);
}
if (status == CELIX_SUCCESS) {
if (function != NULL) {
status = function(handle, reference, service);
} else {
status = bundleContext_getService(tracker->context, reference, service);
}
}
} else {
status = bundleContext_getService(tracker->context, reference, service);
}
framework_logIfError(logger, status, NULL, "Cannot handle addingService");
return status;
}
static celix_status_t serviceTracker_untrack(service_tracker_pt tracker, service_reference_pt reference, service_event_pt event) {
celix_status_t status = CELIX_SUCCESS;
tracked_pt tracked = NULL;
unsigned int i;
bool found = false;
celixThreadRwlock_writeLock(&tracker->lock);
for (i = 0; i < arrayList_size(tracker->trackedServices); i++) {
bool equals;
tracked = (tracked_pt) arrayList_get(tracker->trackedServices, i);
serviceReference_equals(reference, tracked->reference, &equals);
if (equals) {
found = true;
arrayList_remove(tracker->trackedServices, i);
break;
}
}
celixThreadRwlock_unlock(&tracker->lock);
if (found && tracked != NULL) {
serviceTracker_invokeRemovingService(tracker, tracked->reference, tracked->service);
free(tracked);
bundleContext_ungetServiceReference(tracker->context, reference);
}
framework_logIfError(logger, status, NULL, "Cannot untrack reference");
return status;
}
static celix_status_t serviceTracker_invokeRemovingService(service_tracker_pt tracker, service_reference_pt ref, void *service) {
celix_status_t status = CELIX_SUCCESS;
bool ungetSuccess = true;
if (tracker->customizer != NULL) {
void *handle = NULL;
removed_callback_pt function = NULL;
serviceTrackerCustomizer_getHandle(tracker->customizer, &handle);
serviceTrackerCustomizer_getRemovedFunction(tracker->customizer, &function);
if (function != NULL) {
status = function(handle, ref, service);
}
if (status == CELIX_SUCCESS) {
status = bundleContext_ungetService(tracker->context, ref, &ungetSuccess);
}
} else {
status = bundleContext_ungetService(tracker->context, ref, &ungetSuccess);
}
if (!ungetSuccess) {
framework_log(logger, OSGI_FRAMEWORK_LOG_ERROR, __FUNCTION__, __FILE__, __LINE__, "Error ungetting service");
status = CELIX_BUNDLE_EXCEPTION;
}
return status;
}