001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 package org.apache.felix.framework; 020 021 import java.util.*; 022 023 import org.apache.felix.framework.util.FelixConstants; 024 import org.osgi.framework.*; 025 import org.osgi.framework.hooks.service.*; 026 import org.osgi.framework.launch.Framework; 027 028 public class ServiceRegistry 029 { 030 private final Logger m_logger; 031 private long m_currentServiceId = 1L; 032 // Maps bundle to an array of service registrations. 033 private final Map m_serviceRegsMap = Collections.synchronizedMap(new HashMap()); 034 // Maps registration to thread to keep track when a 035 // registration is in use, which will cause other 036 // threads to wait. 037 private Map m_lockedRegsMap = new HashMap(); 038 // Maps bundle to an array of usage counts. 039 private Map m_inUseMap = new HashMap(); 040 041 private final ServiceRegistryCallbacks m_callbacks; 042 043 private final Set m_eventHooks = new TreeSet(Collections.reverseOrder()); 044 private final Set m_findHooks = new TreeSet(Collections.reverseOrder()); 045 private final Set m_listenerHooks = new TreeSet(Collections.reverseOrder()); 046 047 public ServiceRegistry(Logger logger, ServiceRegistryCallbacks callbacks) 048 { 049 m_logger = logger; 050 m_callbacks = callbacks; 051 } 052 053 public ServiceReference[] getRegisteredServices(Bundle bundle) 054 { 055 ServiceRegistration[] regs = (ServiceRegistration[]) m_serviceRegsMap.get(bundle); 056 if (regs != null) 057 { 058 List refs = new ArrayList(regs.length); 059 for (int i = 0; i < regs.length; i++) 060 { 061 try 062 { 063 refs.add(regs[i].getReference()); 064 } 065 catch (IllegalStateException ex) 066 { 067 // Don't include the reference as it is not valid anymore 068 } 069 } 070 return (ServiceReference[]) refs.toArray(new ServiceReference[refs.size()]); 071 } 072 return null; 073 } 074 075 public ServiceRegistration registerService( 076 Bundle bundle, String[] classNames, Object svcObj, Dictionary dict) 077 { 078 ServiceRegistration reg = null; 079 080 synchronized (this) 081 { 082 // Create the service registration. 083 reg = new ServiceRegistrationImpl( 084 this, bundle, classNames, new Long(m_currentServiceId++), svcObj, dict); 085 086 // Keep track of registered hooks. 087 addHooks(classNames, svcObj, reg.getReference()); 088 089 // Get the bundles current registered services. 090 ServiceRegistration[] regs = (ServiceRegistration[]) m_serviceRegsMap.get(bundle); 091 m_serviceRegsMap.put(bundle, addServiceRegistration(regs, reg)); 092 } 093 094 // Notify callback objects about registered service. 095 if (m_callbacks != null) 096 { 097 m_callbacks.serviceChanged( 098 new ServiceEvent(ServiceEvent.REGISTERED, reg.getReference()), null); 099 } 100 return reg; 101 } 102 103 public void unregisterService(Bundle bundle, ServiceRegistration reg) 104 { 105 // If this is a hook, it should be removed. 106 removeHook(reg.getReference()); 107 108 synchronized (this) 109 { 110 // Note that we don't lock the service registration here using 111 // the m_lockedRegsMap because we want to allow bundles to get 112 // the service during the unregistration process. However, since 113 // we do remove the registration from the service registry, no 114 // new bundles will be able to look up the service. 115 116 // Now remove the registered service. 117 ServiceRegistration[] regs = (ServiceRegistration[]) m_serviceRegsMap.get(bundle); 118 m_serviceRegsMap.put(bundle, removeServiceRegistration(regs, reg)); 119 } 120 121 // Notify callback objects about unregistering service. 122 if (m_callbacks != null) 123 { 124 m_callbacks.serviceChanged( 125 new ServiceEvent(ServiceEvent.UNREGISTERING, reg.getReference()), null); 126 } 127 128 // Now forcibly unget the service object for all stubborn clients. 129 synchronized (this) 130 { 131 Bundle[] clients = getUsingBundles(reg.getReference()); 132 for (int i = 0; (clients != null) && (i < clients.length); i++) 133 { 134 while (ungetService(clients[i], reg.getReference())) 135 ; // Keep removing until it is no longer possible 136 } 137 ((ServiceRegistrationImpl) reg).invalidate(); 138 } 139 } 140 141 /** 142 * This method retrieves all services registrations for the specified 143 * bundle and invokes <tt>ServiceRegistration.unregister()</tt> on each 144 * one. This method is only called be the framework to clean up after 145 * a stopped bundle. 146 * @param bundle the bundle whose services should be unregistered. 147 **/ 148 public void unregisterServices(Bundle bundle) 149 { 150 // Simply remove all service registrations for the bundle. 151 ServiceRegistration[] regs = null; 152 synchronized (this) 153 { 154 regs = (ServiceRegistration[]) m_serviceRegsMap.get(bundle); 155 } 156 157 // Note, there is no race condition here with respect to the 158 // bundle registering more services, because its bundle context 159 // has already been invalidated by this point, so it would not 160 // be able to register more services. 161 162 // Unregister each service. 163 for (int i = 0; (regs != null) && (i < regs.length); i++) 164 { 165 if (((ServiceRegistrationImpl) regs[i]).isValid()) 166 { 167 regs[i].unregister(); 168 } 169 } 170 171 // Now remove the bundle itself. 172 synchronized (this) 173 { 174 m_serviceRegsMap.remove(bundle); 175 } 176 } 177 178 public List getServiceReferences(String className, Filter filter) 179 { 180 // Create a filtered list of service references. 181 List list = new ArrayList(); 182 183 Object[] registrations = m_serviceRegsMap.values().toArray(); 184 185 // Iterator over all service registrations. 186 for (int i = 0; i < registrations.length; i++) 187 { 188 ServiceRegistration[] regs = (ServiceRegistration[]) registrations[i]; 189 190 for (int regIdx = 0; 191 (regs != null) && (regIdx < regs.length); 192 regIdx++) 193 { 194 try 195 { 196 // Determine if the registered services matches 197 // the search criteria. 198 boolean matched = false; 199 200 // If className is null, then look at filter only. 201 if ((className == null) && 202 ((filter == null) || filter.match(regs[regIdx].getReference()))) 203 { 204 matched = true; 205 } 206 // If className is not null, then first match the 207 // objectClass property before looking at the 208 // filter. 209 else if (className != null) 210 { 211 String[] objectClass = (String[]) 212 ((ServiceRegistrationImpl) regs[regIdx]) 213 .getProperty(FelixConstants.OBJECTCLASS); 214 for (int classIdx = 0; 215 classIdx < objectClass.length; 216 classIdx++) 217 { 218 if (objectClass[classIdx].equals(className) && 219 ((filter == null) || filter.match(regs[regIdx].getReference()))) 220 { 221 matched = true; 222 break; 223 } 224 } 225 } 226 227 // Add reference if it was a match. 228 if (matched) 229 { 230 list.add(regs[regIdx].getReference()); 231 } 232 } 233 catch (IllegalStateException ex) 234 { 235 // Don't include the reference as it is not valid anymore 236 } 237 } 238 } 239 240 return list; 241 } 242 243 public synchronized ServiceReference[] getServicesInUse(Bundle bundle) 244 { 245 UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle); 246 if (usages != null) 247 { 248 ServiceReference[] refs = new ServiceReference[usages.length]; 249 for (int i = 0; i < refs.length; i++) 250 { 251 refs[i] = usages[i].m_ref; 252 } 253 return refs; 254 } 255 return null; 256 } 257 258 public Object getService(Bundle bundle, ServiceReference ref) 259 { 260 UsageCount usage = null; 261 Object svcObj = null; 262 263 // Get the service registration. 264 ServiceRegistrationImpl reg = 265 ((ServiceRegistrationImpl.ServiceReferenceImpl) ref).getRegistration(); 266 267 synchronized (this) 268 { 269 // First make sure that no existing operation is currently 270 // being performed by another thread on the service registration. 271 for (Object o = m_lockedRegsMap.get(reg); (o != null); o = m_lockedRegsMap.get(reg)) 272 { 273 // We don't allow cycles when we call out to the service factory. 274 if (o.equals(Thread.currentThread())) 275 { 276 throw new IllegalStateException( 277 "ServiceFactory.getService() resulted in a cycle."); 278 } 279 280 // Otherwise, wait for it to be freed. 281 try 282 { 283 wait(); 284 } 285 catch (InterruptedException ex) 286 { 287 } 288 } 289 290 // Lock the service registration. 291 m_lockedRegsMap.put(reg, Thread.currentThread()); 292 293 // Make sure the service registration is still valid. 294 if (reg.isValid()) 295 { 296 // Get the usage count, if any. 297 usage = getUsageCount(bundle, ref); 298 299 // If we don't have a usage count, then create one and 300 // since the spec says we increment usage count before 301 // actually getting the service object. 302 if (usage == null) 303 { 304 usage = addUsageCount(bundle, ref); 305 } 306 307 // Increment the usage count and grab the already retrieved 308 // service object, if one exists. 309 usage.m_count++; 310 svcObj = usage.m_svcObj; 311 } 312 } 313 314 // If we have a usage count, but no service object, then we haven't 315 // cached the service object yet, so we need to create one now without 316 // holding the lock, since we will potentially call out to a service 317 // factory. 318 try 319 { 320 if ((usage != null) && (svcObj == null)) 321 { 322 svcObj = reg.getService(bundle); 323 } 324 } 325 finally 326 { 327 // If we successfully retrieved a service object, then we should 328 // cache it in the usage count. If not, we should flush the usage 329 // count. Either way, we need to unlock the service registration 330 // so that any threads waiting for it can continue. 331 synchronized (this) 332 { 333 // Before caching the service object, double check to see if 334 // the registration is still valid, since it may have been 335 // unregistered while we didn't hold the lock. 336 if (!reg.isValid() || (svcObj == null)) 337 { 338 flushUsageCount(bundle, ref); 339 } 340 else 341 { 342 usage.m_svcObj = svcObj; 343 } 344 m_lockedRegsMap.remove(reg); 345 notifyAll(); 346 } 347 } 348 349 return svcObj; 350 } 351 352 public boolean ungetService(Bundle bundle, ServiceReference ref) 353 { 354 UsageCount usage = null; 355 ServiceRegistrationImpl reg = 356 ((ServiceRegistrationImpl.ServiceReferenceImpl) ref).getRegistration(); 357 358 synchronized (this) 359 { 360 // First make sure that no existing operation is currently 361 // being performed by another thread on the service registration. 362 for (Object o = m_lockedRegsMap.get(reg); (o != null); o = m_lockedRegsMap.get(reg)) 363 { 364 // We don't allow cycles when we call out to the service factory. 365 if (o.equals(Thread.currentThread())) 366 { 367 throw new IllegalStateException( 368 "ServiceFactory.ungetService() resulted in a cycle."); 369 } 370 371 // Otherwise, wait for it to be freed. 372 try 373 { 374 wait(); 375 } 376 catch (InterruptedException ex) 377 { 378 } 379 } 380 381 // Get the usage count. 382 usage = getUsageCount(bundle, ref); 383 // If there is no cached services, then just return immediately. 384 if (usage == null) 385 { 386 return false; 387 } 388 389 // Lock the service registration. 390 m_lockedRegsMap.put(reg, Thread.currentThread()); 391 } 392 393 // If usage count will go to zero, then unget the service 394 // from the registration; we do this outside the lock 395 // since this might call out to the service factory. 396 try 397 { 398 if (usage.m_count == 1) 399 { 400 // Remove reference from usages array. 401 ((ServiceRegistrationImpl.ServiceReferenceImpl) ref) 402 .getRegistration().ungetService(bundle, usage.m_svcObj); 403 } 404 } 405 finally 406 { 407 // Finally, decrement usage count and flush if it goes to zero or 408 // the registration became invalid while we were not holding the 409 // lock. Either way, unlock the service registration so that any 410 // threads waiting for it can continue. 411 synchronized (this) 412 { 413 // Decrement usage count, which spec says should happen after 414 // ungetting the service object. 415 usage.m_count--; 416 417 // If the registration is invalid or the usage count has reached 418 // zero, then flush it. 419 if (!reg.isValid() || (usage.m_count <= 0)) 420 { 421 usage.m_svcObj = null; 422 flushUsageCount(bundle, ref); 423 } 424 425 // Release the registration lock so any waiting threads can 426 // continue. 427 m_lockedRegsMap.remove(reg); 428 notifyAll(); 429 } 430 } 431 432 return true; 433 } 434 435 436 /** 437 * This is a utility method to release all services being 438 * used by the specified bundle. 439 * @param bundle the bundle whose services are to be released. 440 **/ 441 public void ungetServices(Bundle bundle) 442 { 443 UsageCount[] usages; 444 synchronized (this) 445 { 446 usages = (UsageCount[]) m_inUseMap.get(bundle); 447 } 448 449 if (usages == null) 450 { 451 return; 452 } 453 454 // Note, there is no race condition here with respect to the 455 // bundle using more services, because its bundle context 456 // has already been invalidated by this point, so it would not 457 // be able to look up more services. 458 459 // Remove each service object from the 460 // service cache. 461 for (int i = 0; i < usages.length; i++) 462 { 463 // Keep ungetting until all usage count is zero. 464 while (ungetService(bundle, usages[i].m_ref)) 465 { 466 // Empty loop body. 467 } 468 } 469 } 470 471 public synchronized Bundle[] getUsingBundles(ServiceReference ref) 472 { 473 Bundle[] bundles = null; 474 for (Iterator iter = m_inUseMap.entrySet().iterator(); iter.hasNext(); ) 475 { 476 Map.Entry entry = (Map.Entry) iter.next(); 477 Bundle bundle = (Bundle) entry.getKey(); 478 UsageCount[] usages = (UsageCount[]) entry.getValue(); 479 for (int useIdx = 0; useIdx < usages.length; useIdx++) 480 { 481 if (usages[useIdx].m_ref.equals(ref)) 482 { 483 // Add the bundle to the array to be returned. 484 if (bundles == null) 485 { 486 bundles = new Bundle[] { bundle }; 487 } 488 else 489 { 490 Bundle[] nbs = new Bundle[bundles.length + 1]; 491 System.arraycopy(bundles, 0, nbs, 0, bundles.length); 492 nbs[bundles.length] = bundle; 493 bundles = nbs; 494 } 495 } 496 } 497 } 498 return bundles; 499 } 500 501 void servicePropertiesModified(ServiceRegistration reg, Dictionary oldProps) 502 { 503 if (m_callbacks != null) 504 { 505 m_callbacks.serviceChanged( 506 new ServiceEvent(ServiceEvent.MODIFIED, reg.getReference()), oldProps); 507 } 508 } 509 510 public Logger getLogger() 511 { 512 return m_logger; 513 } 514 515 private static ServiceRegistration[] addServiceRegistration( 516 ServiceRegistration[] regs, ServiceRegistration reg) 517 { 518 if (regs == null) 519 { 520 regs = new ServiceRegistration[] { reg }; 521 } 522 else 523 { 524 ServiceRegistration[] newRegs = new ServiceRegistration[regs.length + 1]; 525 System.arraycopy(regs, 0, newRegs, 0, regs.length); 526 newRegs[regs.length] = reg; 527 regs = newRegs; 528 } 529 return regs; 530 } 531 532 private static ServiceRegistration[] removeServiceRegistration( 533 ServiceRegistration[] regs, ServiceRegistration reg) 534 { 535 for (int i = 0; (regs != null) && (i < regs.length); i++) 536 { 537 if (regs[i].equals(reg)) 538 { 539 // If this is the only usage, then point to empty list. 540 if ((regs.length - 1) == 0) 541 { 542 regs = new ServiceRegistration[0]; 543 } 544 // Otherwise, we need to do some array copying. 545 else 546 { 547 ServiceRegistration[] newRegs = new ServiceRegistration[regs.length - 1]; 548 System.arraycopy(regs, 0, newRegs, 0, i); 549 if (i < newRegs.length) 550 { 551 System.arraycopy( 552 regs, i + 1, newRegs, i, newRegs.length - i); 553 } 554 regs = newRegs; 555 } 556 } 557 } 558 return regs; 559 } 560 561 /** 562 * Utility method to retrieve the specified bundle's usage count for the 563 * specified service reference. 564 * @param bundle The bundle whose usage counts are being searched. 565 * @param ref The service reference to find in the bundle's usage counts. 566 * @return The associated usage count or null if not found. 567 **/ 568 private UsageCount getUsageCount(Bundle bundle, ServiceReference ref) 569 { 570 UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle); 571 for (int i = 0; (usages != null) && (i < usages.length); i++) 572 { 573 if (usages[i].m_ref.equals(ref)) 574 { 575 return usages[i]; 576 } 577 } 578 return null; 579 } 580 581 /** 582 * Utility method to update the specified bundle's usage count array to 583 * include the specified service. This method should only be called 584 * to add a usage count for a previously unreferenced service. If the 585 * service already has a usage count, then the existing usage count 586 * counter simply needs to be incremented. 587 * @param bundle The bundle acquiring the service. 588 * @param ref The service reference of the acquired service. 589 * @param svcObj The service object of the acquired service. 590 **/ 591 private UsageCount addUsageCount(Bundle bundle, ServiceReference ref) 592 { 593 UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle); 594 595 UsageCount usage = new UsageCount(); 596 usage.m_ref = ref; 597 598 if (usages == null) 599 { 600 usages = new UsageCount[] { usage }; 601 } 602 else 603 { 604 UsageCount[] newUsages = new UsageCount[usages.length + 1]; 605 System.arraycopy(usages, 0, newUsages, 0, usages.length); 606 newUsages[usages.length] = usage; 607 usages = newUsages; 608 } 609 610 m_inUseMap.put(bundle, usages); 611 612 return usage; 613 } 614 615 /** 616 * Utility method to flush the specified bundle's usage count for the 617 * specified service reference. This should be called to completely 618 * remove the associated usage count object for the specified service 619 * reference. If the goal is to simply decrement the usage, then get 620 * the usage count and decrement its counter. This method will also 621 * remove the specified bundle from the "in use" map if it has no more 622 * usage counts after removing the usage count for the specified service 623 * reference. 624 * @param bundle The bundle whose usage count should be removed. 625 * @param ref The service reference whose usage count should be removed. 626 **/ 627 private void flushUsageCount(Bundle bundle, ServiceReference ref) 628 { 629 UsageCount[] usages = (UsageCount[]) m_inUseMap.get(bundle); 630 for (int i = 0; (usages != null) && (i < usages.length); i++) 631 { 632 if (usages[i].m_ref.equals(ref)) 633 { 634 // If this is the only usage, then point to empty list. 635 if ((usages.length - 1) == 0) 636 { 637 usages = null; 638 } 639 // Otherwise, we need to do some array copying. 640 else 641 { 642 UsageCount[] newUsages = new UsageCount[usages.length - 1]; 643 System.arraycopy(usages, 0, newUsages, 0, i); 644 if (i < newUsages.length) 645 { 646 System.arraycopy( 647 usages, i + 1, newUsages, i, newUsages.length - i); 648 } 649 usages = newUsages; 650 } 651 } 652 } 653 654 if (usages != null) 655 { 656 m_inUseMap.put(bundle, usages); 657 } 658 else 659 { 660 m_inUseMap.remove(bundle); 661 } 662 } 663 664 private void addHooks(String[] classNames, Object svcObj, ServiceReference ref) 665 { 666 if (isHook(classNames, EventHook.class, svcObj)) 667 { 668 synchronized (m_eventHooks) 669 { 670 m_eventHooks.add(ref); 671 } 672 } 673 674 if (isHook(classNames, FindHook.class, svcObj)) 675 { 676 synchronized (m_findHooks) 677 { 678 m_findHooks.add(ref); 679 } 680 } 681 682 if (isHook(classNames, ListenerHook.class, svcObj)) 683 { 684 synchronized (m_listenerHooks) 685 { 686 m_listenerHooks.add(ref); 687 } 688 } 689 } 690 691 static boolean isHook(String[] classNames, Class hookClass, Object svcObj) 692 { 693 if (svcObj instanceof ServiceFactory) 694 { 695 return Arrays.asList(classNames).contains(hookClass.getName()); 696 } 697 698 if (hookClass.isAssignableFrom(svcObj.getClass())) 699 { 700 String hookName = hookClass.getName(); 701 for (int i = 0; i < classNames.length; i++) 702 { 703 if (classNames[i].equals(hookName)) 704 { 705 return true; 706 } 707 } 708 } 709 return false; 710 } 711 712 private void removeHook(ServiceReference ref) 713 { 714 Object svcObj = ((ServiceRegistrationImpl.ServiceReferenceImpl) ref) 715 .getRegistration().getService(); 716 String [] classNames = (String[]) ref.getProperty(Constants.OBJECTCLASS); 717 718 if (isHook(classNames, EventHook.class, svcObj)) 719 { 720 synchronized (m_eventHooks) 721 { 722 m_eventHooks.remove(ref); 723 } 724 } 725 726 if (isHook(classNames, FindHook.class, svcObj)) 727 { 728 synchronized (m_findHooks) 729 { 730 m_findHooks.remove(ref); 731 } 732 } 733 734 if (isHook(classNames, ListenerHook.class, svcObj)) 735 { 736 synchronized (m_listenerHooks) 737 { 738 m_listenerHooks.remove(ref); 739 } 740 } 741 } 742 743 public List getEventHooks() 744 { 745 synchronized (m_eventHooks) 746 { 747 return new ArrayList(m_eventHooks); 748 } 749 } 750 751 List getFindHooks() 752 { 753 synchronized (m_findHooks) 754 { 755 return new ArrayList(m_findHooks); 756 } 757 } 758 759 List getListenerHooks() 760 { 761 synchronized (m_listenerHooks) 762 { 763 return new ArrayList(m_listenerHooks); 764 } 765 } 766 767 /** 768 * Invokes a Service Registry Hook 769 * @param ref The ServiceReference associated with the hook to be invoked, the hook 770 * service object will be obtained through this object. 771 * @param framework The framework that is invoking the hook, typically the Felix object. 772 * @param callback This is a callback object that is invoked with the actual hook object to 773 * be used, either the plain hook, or one obtained from the ServiceFactory. 774 */ 775 public void invokeHook( 776 ServiceReference ref, Framework framework, InvokeHookCallback callback) 777 { 778 Object hook = getService(framework, ref); 779 780 try 781 { 782 callback.invokeHook(hook); 783 } 784 catch (Throwable th) 785 { 786 m_logger.log(ref, Logger.LOG_WARNING, 787 "Problem invoking Service Registry Hook", th); 788 } 789 finally 790 { 791 ungetService(framework, ref); 792 } 793 } 794 795 private static class UsageCount 796 { 797 public int m_count = 0; 798 public ServiceReference m_ref = null; 799 public Object m_svcObj = null; 800 } 801 802 public interface ServiceRegistryCallbacks 803 { 804 void serviceChanged(ServiceEvent event, Dictionary oldProps); 805 } 806 }