/*
* 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.ipojo.context;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.List;
import org.apache.felix.ipojo.ComponentInstance;
import org.apache.felix.ipojo.util.Logger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
* Internal Service Registry. This class is used for in the composition.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class ServiceRegistry {
/**
* Service Id index.
*/
private long m_serviceId = 1L;
/**
* List of service listeners.
*/
private List m_listeners = new ArrayList(); // ListenerInfo List
/**
* List of service registration.
*/
private List m_regs = new ArrayList();
/**
* A "real" bundle context to create LDAP filter.
*/
private BundleContext m_context; // BundleContext to create Filter
/**
* Registry logger.
*/
private Logger m_logger;
/**
* Listener info structure.
*/
private static class ListenerInfo {
/**
* Listener object.
*/
private ServiceListener m_listener;
/**
* Filter associated with the filter.
*/
private Filter m_filter;
}
/**
* Constructor.
*
* @param context : bundle context.
*/
public ServiceRegistry(BundleContext context) {
m_context = context;
m_logger = new Logger(m_context, "Registry logger " + m_context.getBundle().getBundleId());
}
/**
* Add a given service listener with no filter.
*
* @param arg0 : the service listener to add
*/
public void addServiceListener(ServiceListener arg0) {
ListenerInfo info = new ListenerInfo();
info.m_listener = arg0;
info.m_filter = null;
m_listeners.add(info);
}
/**
* Unget a service.
*
* @param instance : instance releasing the service.
* @param ref : released reference.
* @return true if the unget success
*/
public boolean ungetService(ComponentInstance instance, ServiceReference ref) {
ServiceRegistrationImpl reg = ((ServiceReferenceImpl) ref).getServiceRegistration();
if (reg.isValid()) {
reg.ungetService(instance, reg.getService());
return true;
} else {
return false;
}
}
/**
* Unregister a service listener.
*
* @param arg0 : the service listener to remove
*/
public void removeServiceListener(ServiceListener arg0) {
m_listeners.remove(arg0);
}
/**
* Register a service.
*
* @param instance : provider instance.
* @param clazz : provided interface.
* @param svcObj : service object of service factory object.
* @param dict : service properties.
* @return the created service registration.
*/
public ServiceRegistration registerService(ComponentInstance instance, String clazz, Object svcObj, Dictionary dict) {
synchronized (m_regs) {
ServiceRegistrationImpl reg = new ServiceRegistrationImpl(this, instance, new String[] { clazz }, new Long(m_serviceId++), svcObj, dict);
m_regs.add(reg);
fireServiceChanged(new ServiceEvent(ServiceEvent.REGISTERED, reg.getReference()));
return reg;
}
}
/**
* Register a service.
*
* @param instance : provider instance.
* @param clazzes : provided interfaces.
* @param svcObj : service object of service factory object.
* @param dict : service properties.
* @return the created service registration.
*/
public ServiceRegistration registerService(ComponentInstance instance, String[] clazzes, Object svcObj, Dictionary dict) {
synchronized (m_regs) {
ServiceRegistrationImpl reg = new ServiceRegistrationImpl(this, instance, clazzes, new Long(m_serviceId++), svcObj, dict);
m_regs.add(reg);
fireServiceChanged(new ServiceEvent(ServiceEvent.REGISTERED, reg.getReference()));
return reg;
}
}
/**
* Dispatch a service event.
* @param event : the service to dispatch
*/
private void fireServiceChanged(ServiceEvent event) {
synchronized (m_listeners) {
// Iterate on the service listener list to notify service listener
for (int i = 0; i < m_listeners.size(); i++) {
ListenerInfo info = (ListenerInfo) m_listeners.get(i);
ServiceReference ref = event.getServiceReference();
if (info.m_filter == null) {
info.m_listener.serviceChanged(event);
}
Dictionary props = ((ServiceReferenceImpl) ref).getProperties();
if (info.m_filter != null && info.m_filter.match(props)) {
info.m_listener.serviceChanged(event);
}
}
}
}
/**
* Get available (and accessible) service references.
*
* @param className : required interface
* @param expr : LDAP filter
* @return : the list of available service references.
* @throws InvalidSyntaxException
* occurs when the LDAP filter is malformed.
*/
public ServiceReference[] getServiceReferences(String className, String expr) throws InvalidSyntaxException {
synchronized (m_regs) {
// Define filter if expression is not null.
Filter filter = null;
if (expr != null) {
filter = m_context.createFilter(expr);
}
List refs = new ArrayList();
for (int i = 0; i < m_regs.size(); i++) {
ServiceRegistrationImpl reg = (ServiceRegistrationImpl) m_regs.get(i);
// Determine if the registered services matches the search
// criteria.
boolean matched = false;
// If className is null, then look at filter only.
if ((className == null) && ((filter == null) || filter.match(reg.getProperties()))) {
matched = true;
} else if (className != null) {
// If className is not null, then first match the
// objectClass property before looking at the
// filter.
Dictionary props = ((ServiceRegistrationImpl) reg).getProperties();
String[] objectClass = (String[]) props.get(Constants.OBJECTCLASS);
for (int classIdx = 0; classIdx < objectClass.length; classIdx++) {
if (objectClass[classIdx].equals(className) && ((filter == null) || filter.match(props))) {
matched = true;
break;
}
}
}
// Add reference if it was a match.
if (matched) {
refs.add(reg.getReference());
}
}
if (! refs.isEmpty()) {
return (ServiceReference[]) refs.toArray(new ServiceReference[refs.size()]);
}
return null;
}
}
/**
* Look for a service reference.
*
* @param clazz : required interface.
* @return the first available provider or null if none available.
*/
public ServiceReference getServiceReference(String clazz) {
synchronized (m_regs) {
try {
ServiceReference[] refs = getServiceReferences(clazz, null);
if (refs != null) {
return refs[0];
} // If the refs != null we are sure that it exists one reference or more.
} catch (InvalidSyntaxException ex) {
// Cannot happen : null filter.
}
return null;
}
}
/**
* Get a service object.
* @param instance : component instance requiring the service.
* @param ref : the required reference.
* @return the service object.
*/
public Object getService(ComponentInstance instance, ServiceReference ref) {
synchronized (m_regs) {
// Look for the service registration for this ref
ServiceRegistrationImpl reg = ((ServiceReferenceImpl) ref).getServiceRegistration();
if (reg.isValid()) {
// Delegate the service providing to the service registration
return reg.getService();
} else {
return null;
}
}
}
/**
* Get all service references consistent with the given interface and
* filter.
* @param clazz : the required interface.
* @param filter : the LDAP filter.
* @return the list of all service reference or null if none available.
* @throws InvalidSyntaxException occurs when the LDAP filter is malformed.
*/
public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
synchronized (m_regs) {
// Can delegate on getServiceReference, indeed their is no test on
// the "modularity" conflict.
return getServiceReferences(clazz, filter);
}
}
/**
* Add a service listener with a filter.
* @param listener : the service listener to add
* @param filter : LDAP filter
*/
public void addServiceListener(ServiceListener listener, String filter) {
// If the filter is null, subscribe with no filter.
if (filter == null) {
addServiceListener(listener);
return;
}
try {
ListenerInfo info = new ListenerInfo();
info.m_listener = listener;
info.m_filter = m_context.createFilter(filter);
m_listeners.add(info);
} catch (InvalidSyntaxException ex) {
m_logger.log(Logger.ERROR, ex.getMessage(), ex);
}
}
/**
* Dispatch a service properties modified event.
* @param reg : the implicated service registration.
*/
public void servicePropertiesModified(ServiceRegistrationImpl reg) {
fireServiceChanged(new ServiceEvent(ServiceEvent.MODIFIED, reg.getReference()));
}
/**
* Unregister a service.
* @param reg : the service registration to unregister
*/
public void unregisterService(ServiceRegistrationImpl reg) {
m_regs.remove(reg);
fireServiceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, reg.getReference()));
}
/**
* Reset the service registry.
*/
public void reset() {
m_serviceId = 1L;
m_listeners = new ArrayList();
m_regs = new ArrayList();
}
}