blob: 02f206b08d4166ac1f98523cd7ccdc68e7c1eead [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation
*
* 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.apache.osgi.framework;
import java.util.*;
import org.apache.osgi.framework.util.FelixConstants;
import org.osgi.framework.*;
public class ServiceRegistry
{
private LogWrapper m_logger = null;
private long m_currentServiceId = 1L;
// Maps bundle to an array of service registrations.
private Map m_serviceRegsMap = new HashMap();
// Maps bundle to an array of usage counts.
private Map m_inUseMap = new HashMap();
private ServiceListener m_serviceListener = null;
public ServiceRegistry(LogWrapper logger)
{
m_logger = logger;
}
public synchronized ServiceReference[] getRegisteredServices(Bundle bundle)
{
ServiceRegistration[] regs = (ServiceRegistration[]) m_serviceRegsMap.get(bundle);
if (regs != null)
{
ServiceReference[] refs = new ServiceReference[regs.length];
for (int i = 0; i < refs.length; i++)
{
refs[i] = regs[i].getReference();
}
return refs;
}
return null;
}
public ServiceRegistration registerService(
Bundle bundle, String[] classNames, Object svcObj, Dictionary dict)
{
ServiceRegistration reg = null;
synchronized (this)
{
// Create the service registration.
reg = new ServiceRegistrationImpl(
this, bundle, classNames, new Long(m_currentServiceId++), svcObj, dict);
// Get the bundles current registered services.
ServiceRegistration[] regs = (ServiceRegistration[]) m_serviceRegsMap.get(bundle);
m_serviceRegsMap.put(bundle, addServiceRegistration(regs, reg));
}
fireServiceChanged(new ServiceEvent(ServiceEvent.REGISTERED, reg.getReference()));
return reg;
}
public void unregisterService(Bundle bundle, ServiceRegistration reg)
{
synchronized (this)
{
ServiceRegistration[] regs = (ServiceRegistration[]) m_serviceRegsMap.get(bundle);
m_serviceRegsMap.put(bundle, removeServiceRegistration(regs, reg));
}
fireServiceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, reg.getReference()));
}
public void unregisterServices(Bundle bundle)
{
// Simply remove all service registrations for the bundle.
ServiceRegistration[] regs = null;
synchronized (this)
{
regs = (ServiceRegistration[]) m_serviceRegsMap.get(bundle);
m_serviceRegsMap.remove(bundle);
}
// Fire all events outside of synchronized block.
for (int i = 0; (regs != null) && (i < regs.length); i++)
{
fireServiceChanged(
new ServiceEvent(ServiceEvent.UNREGISTERING, regs[i].getReference()));
}
}
public synchronized List getServiceReferences(String className, Filter filter)
throws InvalidSyntaxException
{
// Create a filtered list of service references.
List list = new ArrayList();
// Iterator over all service registrations.
for (Iterator i = m_serviceRegsMap.entrySet().iterator(); i.hasNext(); )
{
Map.Entry entry = (Map.Entry) i.next();
ServiceRegistration[] regs = (ServiceRegistration[]) entry.getValue();
for (int regIdx = 0;
(regs != null) && (regIdx < regs.length);
regIdx++)
{
// 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(regs[regIdx].getReference())))
{
matched = true;
}
// If className is not null, then first match the
// objectClass property before looking at the
// filter.
else if (className != null)
{
String[] objectClass = (String[])
((ServiceRegistrationImpl) regs[regIdx]).getProperty(FelixConstants.OBJECTCLASS);
for (int classIdx = 0;
classIdx < objectClass.length;
classIdx++)
{
if (objectClass[classIdx].equals(className) &&
((filter == null) || filter.match(regs[regIdx].getReference())))
{
matched = true;
break;
}
}
}
// Add reference if it was a match.
if (matched)
{
list.add(regs[regIdx].getReference());
}
}
}
return list;
}
public synchronized ServiceReference[] getServicesInUse(Bundle bundle)
{
UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
if (usages != null)
{
ServiceReference[] refs = new ServiceReference[usages.length];
for (int i = 0; i < refs.length; i++)
{
refs[i] = usages[i].m_ref;
}
return refs;
}
return null;
}
public synchronized Object getService(Bundle bundle, ServiceReference ref)
{
// Get usage counts for specified bundle.
UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
// Make sure the service registration is still valid.
if (!((ServiceReferenceImpl) ref).getServiceRegistration().isValid())
{
// If the service registration is not valid, then this means
// that the service provider unregistered the service. The spec
// says that calls to get an unregistered service should always
// return null (assumption: even if it is currently cached
// by the bundle). So in this case, flush the service reference
// from the cache and return null.
m_inUseMap.put(bundle, removeUsageCount(usages, ref));
// It is not necessary to unget the service object from
// the providing bundle, since the associated service is
// unregistered and hence not in the list of registered services
// of the providing bundle. This is precisely why the service
// registration was not found above in the first place.
return null;
}
// Get the service registration.
ServiceRegistrationImpl reg = ((ServiceReferenceImpl) ref).getServiceRegistration();
// Get the usage count, if any.
UsageCount usage = getUsageCount(usages, ref);
// If the service object is cached, then increase the usage
// count and return the cached service object.
Object svcObj = null;
if (usage != null)
{
usage.m_count++;
svcObj = usage.m_svcObj;
}
else
{
// Get service object from service registration.
svcObj = reg.getService(bundle);
// Cache the service object.
if (svcObj != null)
{
m_inUseMap.put(bundle, addUsageCount(usages, ref, svcObj));
}
}
return svcObj;
}
public synchronized boolean ungetService(Bundle bundle, ServiceReference ref)
{
// Get usage count.
UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
UsageCount usage = getUsageCount(usages, ref);
// If no usage count, then return.
if (usage == null)
{
return false;
}
// Make sure the service registration is still valid.
if (!((ServiceReferenceImpl) ref).getServiceRegistration().isValid())
{
// If the service registration is not valid, then this means
// that the service provider unregistered the service. The spec
// says that calls to get an unregistered service should always
// return null (assumption: even if it is currently cached
// by the bundle). So in this case, flush the service reference
// from the cache and return null.
m_inUseMap.put(bundle, removeUsageCount(usages, ref));
return false;
}
// Decrement usage count.
usage.m_count--;
// Remove reference when usage count goes to zero
// and unget the service object from the exporting
// bundle.
if (usage.m_count == 0)
{
m_inUseMap.put(bundle, removeUsageCount(usages, ref));
ServiceRegistrationImpl reg =
((ServiceReferenceImpl) ref).getServiceRegistration();
reg.ungetService(bundle, usage.m_svcObj);
usage.m_svcObj = null;
}
// Return true if the usage count is greater than zero.
return (usage.m_count > 0);
}
/**
* This is a utility method to release all services being
* used by the specified bundle.
* @param bundle the bundle whose services are to be released.
**/
public synchronized void ungetServices(Bundle bundle)
{
UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle);
if (usages == null)
{
return;
}
// Remove each service object from the
// service cache.
for (int i = 0; i < usages.length; i++)
{
// Keep ungetting until all usage count is zero.
while (ungetService(bundle, usages[i].m_ref))
{
// Empty loop body.
}
}
}
public synchronized Bundle[] getUsingBundles(ServiceReference ref)
{
Bundle[] bundles = null;
for (Iterator iter = m_inUseMap.entrySet().iterator(); iter.hasNext(); )
{
Map.Entry entry = (Map.Entry) iter.next();
Bundle bundle = (Bundle) entry.getKey();
UsageCount[] usages = (UsageCount[]) entry.getValue();
for (int useIdx = 0; useIdx < usages.length; useIdx++)
{
if (usages[useIdx].m_ref.equals(ref))
{
// Add the bundle to the array to be returned.
if (bundles == null)
{
bundles = new Bundle[] { bundle };
}
else
{
Bundle[] nbs = new Bundle[bundles.length + 1];
System.arraycopy(bundles, 0, nbs, 0, bundles.length);
nbs[bundles.length] = bundle;
bundles = nbs;
}
}
}
}
return bundles;
}
public void servicePropertiesModified(ServiceRegistration reg)
{
fireServiceChanged(new ServiceEvent(ServiceEvent.MODIFIED, reg.getReference()));
}
public LogWrapper getLogger()
{
return m_logger;
}
private static ServiceRegistration[] addServiceRegistration(
ServiceRegistration[] regs, ServiceRegistration reg)
{
if (regs == null)
{
regs = new ServiceRegistration[] { reg };
}
else
{
ServiceRegistration[] newRegs = new ServiceRegistration[regs.length + 1];
System.arraycopy(regs, 0, newRegs, 0, regs.length);
newRegs[regs.length] = reg;
regs = newRegs;
}
return regs;
}
private static ServiceRegistration[] removeServiceRegistration(
ServiceRegistration[] regs, ServiceRegistration reg)
{
for (int i = 0; (regs != null) && (i < regs.length); i++)
{
if (regs[i].equals(reg))
{
// If this is the only usage, then point to empty list.
if ((regs.length - 1) == 0)
{
regs = new ServiceRegistration[0];
}
// Otherwise, we need to do some array copying.
else
{
ServiceRegistration[] newRegs = new ServiceRegistration[regs.length - 1];
System.arraycopy(regs, 0, newRegs, 0, i);
if (i < newRegs.length)
{
System.arraycopy(
regs, i + 1, newRegs, i, newRegs.length - i);
}
regs = newRegs;
}
}
}
return regs;
}
public synchronized void addServiceListener(ServiceListener l)
{
m_serviceListener = ServiceListenerMulticaster.add(m_serviceListener, l);
}
public synchronized void removeServiceListener(ServiceListener l)
{
m_serviceListener = ServiceListenerMulticaster.remove(m_serviceListener, l);
}
protected void fireServiceChanged(ServiceEvent event)
{
// Grab a copy of the listener list.
ServiceListener listener = m_serviceListener;
// If not null, then dispatch event.
if (listener != null)
{
m_serviceListener.serviceChanged(event);
}
}
private static class ServiceListenerMulticaster implements ServiceListener
{
protected ServiceListener m_a = null, m_b = null;
protected ServiceListenerMulticaster(ServiceListener a, ServiceListener b)
{
m_a = a;
m_b = b;
}
public void serviceChanged(ServiceEvent e)
{
m_a.serviceChanged(e);
m_b.serviceChanged(e);
}
public static ServiceListener add(ServiceListener a, ServiceListener b)
{
if (a == null)
{
return b;
}
else if (b == null)
{
return a;
}
else
{
return new ServiceListenerMulticaster(a, b);
}
}
public static ServiceListener remove(ServiceListener a, ServiceListener b)
{
if ((a == null) || (a == b))
{
return null;
}
else if (a instanceof ServiceListenerMulticaster)
{
return add(
remove(((ServiceListenerMulticaster) a).m_a, b),
remove(((ServiceListenerMulticaster) a).m_b, b));
}
else
{
return a;
}
}
}
private static UsageCount getUsageCount(UsageCount[] usages, ServiceReference ref)
{
for (int i = 0; (usages != null) && (i < usages.length); i++)
{
if (usages[i].m_ref.equals(ref))
{
return usages[i];
}
}
return null;
}
private static UsageCount[] addUsageCount(UsageCount[] usages, ServiceReference ref, Object svcObj)
{
UsageCount usage = new UsageCount();
usage.m_ref = ref;
usage.m_svcObj = svcObj;
usage.m_count++;
if (usages == null)
{
usages = new UsageCount[] { usage };
}
else
{
UsageCount[] newUsages = new UsageCount[usages.length + 1];
System.arraycopy(usages, 0, newUsages, 0, usages.length);
newUsages[usages.length] = usage;
usages = newUsages;
}
return usages;
}
private static UsageCount[] removeUsageCount(UsageCount[] usages, ServiceReference ref)
{
for (int i = 0; (usages != null) && (i < usages.length); i++)
{
if (usages[i].m_ref.equals(ref))
{
// If this is the only usage, then point to empty list.
if ((usages.length - 1) == 0)
{
usages = new UsageCount[0];
}
// Otherwise, we need to do some array copying.
else
{
UsageCount[] newUsages= new UsageCount[usages.length - 1];
System.arraycopy(usages, 0, newUsages, 0, i);
if (i < newUsages.length)
{
System.arraycopy(
usages, i + 1, newUsages, i, newUsages.length - i);
}
usages = newUsages;
}
}
}
return usages;
}
private static class UsageCount
{
public int m_count = 0;
public ServiceReference m_ref = null;
public Object m_svcObj = null;
}
}