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 020package org.apache.felix.ipojo.handlers.dependency; 021 022import org.apache.felix.ipojo.*; 023import org.apache.felix.ipojo.handlers.dependency.ServiceUsage.Usage; 024import org.apache.felix.ipojo.util.DependencyModel; 025import org.apache.felix.ipojo.util.Log; 026import org.osgi.framework.BundleContext; 027import org.osgi.framework.Filter; 028import org.osgi.framework.ServiceReference; 029 030import java.lang.reflect.*; 031import java.util.*; 032 033/** 034 * Represent a service dependency of the component instance. 035 * 036 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> 037 */ 038public class Dependency extends DependencyModel implements FieldInterceptor, MethodInterceptor, 039 ConstructorInjector { 040 041 /** 042 * Reference on the Dependency Handler. 043 */ 044 private final DependencyHandler m_handler; 045 /** 046 * Field of the dependency. 047 */ 048 private final String m_field; 049 /** 050 * Default-Implementation. 051 */ 052 private final String m_di; 053 /** 054 * Exception to throw when no providers are available. 055 */ 056 private final String m_exception; 057 /** 058 * Is the Nullable pattern enabled? 059 */ 060 private final boolean m_supportNullable; 061 /** 062 * List of dependency callback. 063 * Immutable once set. 064 */ 065 private DependencyCallback[] m_callbacks; 066 /** 067 * Is the dependency a service level dependency. 068 * Immutable once set. 069 */ 070 private boolean m_isServiceLevelRequirement; 071 /** 072 * Is the provider set frozen ? 073 */ 074 private boolean m_isFrozen; 075 /** 076 * Is the dependency started ? 077 */ 078 private boolean m_isStarted; 079 /** 080 * Thread Local. 081 */ 082 private ServiceUsage m_usage; 083 /** 084 * Type of the object to inject in aggregate dependency. This value is used to determine what kind of object need 085 * to be injected for fields and constructor parameter for aggregate dependencies. 086 * Cannot change once set. 087 */ 088 private AggregateDependencyInjectionType m_type; 089 /** 090 * Nullable object. 091 * Immutable once set. 092 */ 093 private Object m_nullable; 094 /** 095 * Id of the dependency. 096 * Immutable once set. 097 */ 098 private String m_id; 099 /** 100 * Do we have to inject proxy? 101 */ 102 private boolean m_isProxy; 103 /** 104 * Proxy Object. 105 */ 106 private Object m_proxyObject; 107 /** 108 * Constructor parameter index. 109 * -1 if not used. 110 */ 111 private int m_index = -1; 112 113 /** 114 * The dependency timeout. 115 */ 116 private int m_timeout; 117 118 /** 119 * Dependency constructor. After the creation the dependency is not started. 120 * 121 * @param handler : the dependency handler managing this dependency 122 * @param field : field of the dependency 123 * @param spec : required specification 124 * @param filter : LDAP filter of the dependency 125 * @param isOptional : is the dependency an optional dependency ? 126 * @param isAggregate : is the dependency an aggregate dependency 127 * @param nullable : describe if the nullable ability is enable or disable 128 * @param isProxy : is the proxied dependency 129 * @param identity : id of the dependency, may be null 130 * @param context : bundle context (or service context) to use. 131 * @param policy : resolution policy 132 * @param cmp : comparator to sort references 133 * @param defaultImplementation : default-implementation class 134 */ 135 public Dependency(DependencyHandler handler, String field, Class spec, Filter filter, boolean isOptional, 136 boolean isAggregate, boolean nullable, boolean isProxy, String identity, BundleContext context, 137 int policy, Comparator cmp, String defaultImplementation, String exception) { 138 super(spec, isAggregate, isOptional, filter, cmp, policy, context, handler, handler.getInstanceManager()); 139 m_handler = handler; 140 m_field = field; 141 m_isProxy = isProxy; 142 143 if (field != null) { 144 m_usage = new ServiceUsage(); 145 } else { 146 m_usage = null; 147 } 148 149 m_supportNullable = nullable; 150 m_di = defaultImplementation; 151 m_exception = exception; 152 153 if (identity == null) { 154 if (spec != null) { 155 m_id = spec.getName(); 156 } 157 } else { 158 m_id = identity; 159 } 160 161 // Else wait the setSpecification call. 162 } 163 164 /** 165 * Set the specification of the current dependency. 166 * In order to store the id of the dependency, this 167 * method is override. This method is called during the 168 * configuration. 169 * 170 * @param spec : request service Class 171 * @see org.apache.felix.ipojo.util.DependencyModel#setSpecification(java.lang.Class) 172 */ 173 public void setSpecification(Class spec) { 174 super.setSpecification(spec); 175 if (m_id == null) { 176 m_id = spec.getName(); 177 } 178 } 179 180 public String getField() { 181 return m_field; 182 } 183 184 /** 185 * Add a callback to the dependency. 186 * This method is called during the configuration. 187 * 188 * @param callback : callback to add 189 */ 190 protected void addDependencyCallback(DependencyCallback callback) { 191 if (m_callbacks == null) { 192 m_callbacks = new DependencyCallback[]{callback}; 193 } else { 194 DependencyCallback[] newCallbacks = new DependencyCallback[m_callbacks.length + 1]; 195 System.arraycopy(m_callbacks, 0, newCallbacks, 0, m_callbacks.length); 196 newCallbacks[m_callbacks.length] = callback; 197 m_callbacks = newCallbacks; 198 } 199 } 200 201 protected void addConstructorInjection(int index) throws ConfigurationException { 202 m_index = index; 203 m_usage = new ServiceUsage(); 204 m_handler.getInstanceManager().register(index, this); 205 } 206 207 /** 208 * Stop the current dependency. 209 * 210 * @see org.apache.felix.ipojo.util.DependencyModel#stop() 211 */ 212 public void stop() { 213 acquireWriteLockIfNotHeld(); 214 m_isStarted = false; 215 super.stop(); 216 releaseWriteLockIfHeld(); 217 218 } 219 220 public DependencyHandler getHandler() { 221 return m_handler; 222 } 223 224 public boolean isFrozen() { 225 try { 226 acquireReadLockIfNotHeld(); 227 return m_isFrozen; 228 } finally { 229 releaseReadLockIfHeld(); 230 } 231 } 232 233 /** 234 * Unfreeze the dependency. 235 * 236 * @see org.apache.felix.ipojo.util.DependencyModel#unfreeze() 237 */ 238 public void unfreeze() { 239 try { 240 acquireWriteLockIfNotHeld(); 241 m_isFrozen = false; 242 } finally { 243 releaseWriteLockIfHeld(); 244 } 245 } 246 247 /** 248 * Call the bind method. 249 * 250 * @param pojo : pojo instance on which calling the bind method. 251 */ 252 protected void onObjectCreation(Object pojo) { 253 254 ServiceReference[] refs; 255 try { 256 acquireWriteLockIfNotHeld(); 257 if (!m_isStarted) { 258 return; 259 } 260 261 // We are notified of an instance creation, we have to freeze when the static policy is used 262 if (getBindingPolicy() == STATIC_BINDING_POLICY) { 263 m_isFrozen = true; 264 } 265 266 // Check optional case : nullable object case : do not call bind on nullable object 267 if (isOptional() && getSize() == 0) { 268 return; 269 } 270 271 refs = getServiceReferences(); // Stack confinement. 272 } finally { 273 releaseWriteLockIfHeld(); 274 } 275 276 // This is a pretty strange case, but we don't have any service. 277 // This may happen during refresh. 278 // So we just return. 279 if (refs == null) { 280 return; 281 } 282 283 // Call bind callback. 284 for (int j = 0; m_callbacks != null && j < m_callbacks.length; j++) { // The array is constant. 285 if (m_callbacks[j].getMethodType() == DependencyCallback.BIND) { 286 if (isAggregate()) { 287 for (ServiceReference ref : refs) { 288 Object svc = getService(ref); 289 if (svc != null) { 290 invokeCallback(m_callbacks[j], ref, svc, pojo); 291 } else { 292 // The service left already, or the service object cannot be created. 293 // We consider it as a departure. 294 m_serviceReferenceManager.removedService(ref, null); 295 } 296 } 297 } else { 298 // Take the first reference. 299 Object svc = getService(refs[0]); 300 if (svc != null) { 301 invokeCallback(m_callbacks[j], refs[0], svc, pojo); 302 } else { 303 // The service left already, or the service object cannot be created. 304 // We consider it as a departure. 305 m_serviceReferenceManager.removedService(refs[0], null); 306 } 307 } 308 } 309 } 310 } 311 312 /** 313 * Call unbind callback method. 314 * 315 * @param ref : reference to send (if accepted) to the method 316 */ 317 private void callUnbindMethod(ServiceReference ref) { 318 if (m_handler.getInstanceManager().getState() > InstanceManager.STOPPED && m_handler.getInstanceManager().getPojoObjects() != null) { 319 for (int i = 0; m_callbacks != null && i < m_callbacks.length; i++) { 320 if (m_callbacks[i].getMethodType() == DependencyCallback.UNBIND) { 321 invokeCallback(m_callbacks[i], ref, getService(ref, false), null); // Call on each created pojo objects. 322 } 323 } 324 } 325 } 326 327 /** 328 * Helper method calling the given callback. 329 * 330 * @param callback : callback to call. 331 * @param ref : service reference. 332 * @param svcObject : the service object 333 * @param pojo : pojo on which calling the callback, if null call on each created pojo objects. 334 */ 335 private void invokeCallback(DependencyCallback callback, ServiceReference ref, Object svcObject, Object pojo) { 336 try { 337 if (pojo == null) { 338 callback.call(ref, svcObject); 339 } else { 340 callback.callOnInstance(pojo, ref, svcObject); 341 } 342 } catch (NoSuchMethodException e) { 343 m_handler.error("The method " + callback.getMethodName() + " does not exist in the implementation class " + m_handler.getInstanceManager().getClassName(), e); 344 m_handler.getInstanceManager().stop(); 345 } catch (IllegalAccessException e) { 346 m_handler.error("The method " + callback.getMethodName() + " is not accessible in the implementation class " + m_handler.getInstanceManager().getClassName(), e); 347 m_handler.getInstanceManager().stop(); 348 } catch (InvocationTargetException e) { 349 m_handler.error("The method " + callback.getMethodName() + " in the implementation class " + m_handler.getInstanceManager().getClassName() + " throws an exception : " + e.getTargetException().getMessage(), e.getTargetException()); 350 m_handler.getInstanceManager().stop(); 351 } 352 353 } 354 355 /** 356 * Call 'modify' method with the service reference in parameter (if accepted). 357 * 358 * @param ref : the service reference of the modified service 359 */ 360 private void callModifyMethod(ServiceReference ref) { 361 if (m_handler.getInstanceManager().getState() > InstanceManager.STOPPED && m_handler.getInstanceManager().getPojoObjects() != null) { 362 for (int i = 0; m_callbacks != null && i < m_callbacks.length; i++) { 363 if (m_callbacks[i].getMethodType() == DependencyCallback.MODIFIED) { 364 invokeCallback(m_callbacks[i], ref, getService(ref), null); // Call on each created pojo objects. 365 } 366 } 367 } 368 } 369 370 /** 371 * Call method with the service reference in parameter (if accepted). 372 * 373 * @param ref : the service reference of the new service 374 */ 375 private void callBindMethod(ServiceReference ref) { 376 // call bind method : 377 // if (m_handler.getInstanceManager().getState() == InstanceManager.VALID) { 378 if (m_handler.getInstanceManager().getState() > InstanceManager.STOPPED && m_handler.getInstanceManager().getPojoObjects() != null) { 379 for (int i = 0; m_callbacks != null && i < m_callbacks.length; i++) { 380 if (m_callbacks[i].getMethodType() == DependencyCallback.BIND) { 381 Object svc = getService(ref); 382 if (svc != null) { 383 invokeCallback(m_callbacks[i], ref, svc, null); 384 } else { 385 // We can't get the service object (https://issues.apache.org/jira/browse/FELIX-3896). 386 // This is probably because the service is leaving. 387 // We consider it as a departure. 388 m_serviceReferenceManager.removedService(ref, null); 389 } 390 } 391 } 392 } 393 } 394 395 private RuntimeException createExceptionToThrow() { 396 final String message = "No service available for " + DependencyHandler.getDependencyIdentifier(this); 397 if (m_exception == null) { 398 // Should never happen, but let's see. 399 return new RuntimeException(message); 400 } 401 try { 402 Class<RuntimeException> exceptionClass = (Class<RuntimeException>) getBundleContext() 403 .getBundle().loadClass(m_exception); 404 // Check constructor 405 final Constructor<RuntimeException> constructor = exceptionClass.getConstructor(new Class[]{String.class}); 406 if (constructor != null) { 407 return constructor.newInstance(message); 408 } else { 409 return exceptionClass.newInstance(); 410 } 411 } catch (Exception e) { 412 m_handler.getLogger().log(Log.ERROR, "Cannot create the exception object for dependency " + 413 DependencyHandler.getDependencyIdentifier(this) + " : " + e.getMessage(), e); 414 } 415 416 return new RuntimeException(message); 417 } 418 419 private Object createNullableObject() { 420 // To load the proxy we use the POJO class loader. Indeed, this classloader imports iPOJO (so can access to Nullable) and has 421 // access to the service specification. 422 if ( ! getSpecification().isInterface()) { 423 getHandler().getLogger().log(Log.INFO, "Cannot create the nullable object for " + getSpecification() 424 .getName() + " - the specification is not an interface"); 425 return null; 426 } 427 428 try { 429 ClassLoader cl = new NullableClassLoader( 430 getHandler().getInstanceManager().getClazz().getClassLoader(), 431 getSpecification().getClassLoader()); 432 433 m_nullable = 434 Proxy.newProxyInstance(cl, new Class[]{ 435 getSpecification(), Nullable.class}, new NullableObject()); // NOPMD 436 437 } catch (NoClassDefFoundError e) { 438 // A NoClassDefFoundError is thrown if the specification uses a class not accessible by the actual instance. 439 // It generally comes from a missing import. 440 throw new IllegalStateException("Cannot create the Nullable object, a referenced class cannot be loaded", e); 441 } catch (Throwable e) { // Catch any other exception that can occurs 442 throw new IllegalStateException("Cannot create the Nullable object, an unexpected error occurs", e); 443 } 444 445 return m_nullable; 446 } 447 448 /** 449 * Start the dependency. 450 */ 451 public void start() { 452 453 if (isOptional() && !isAggregate()) { 454 if (m_di == null && m_exception == null) { 455 // If nullable are supported, create the nullable object. 456 if (m_supportNullable) { 457 createNullableObject(); 458 } 459 } else if (m_di != null) { 460 // Create the default-implementation object. 461 try { 462 Class clazz = getHandler().getInstanceManager().getContext().getBundle().loadClass(m_di); 463 m_nullable = clazz.newInstance(); 464 } catch (IllegalAccessException e) { 465 throw new IllegalStateException("Cannot load the default-implementation " + m_di, e); 466 } catch (InstantiationException e) { 467 throw new IllegalStateException("Cannot load the default-implementation " + m_di, e); 468 } catch (ClassNotFoundException e) { 469 throw new IllegalStateException("Cannot load the default-implementation " + m_di, e); 470 } catch (Throwable e) { // Catch any other exception 471 throw new IllegalStateException("Cannot load the default-implementation (unexpected exception) " + m_di, e); 472 } 473 } 474 } 475 476 if (m_isProxy) { 477 if (isAggregate()) { 478 if (m_type == AggregateDependencyInjectionType.SET) { 479 m_proxyObject = new ServiceSet(this); 480 } else { 481 m_proxyObject = new ServiceList(this); 482 } 483 } else { 484 // Can we really proxy ? We can proxy only interfaces. 485 if (getSpecification().isInterface()) { 486 String type = getHandler().getInstanceManager().getContext().getProperty(DependencyHandler.PROXY_TYPE_PROPERTY); 487 488 // If it's null we should check on the System directly, Felix delegates to it, 489 // but not other frameworks 490 if (type == null) { 491 type = System.getProperty(DependencyHandler.PROXY_TYPE_PROPERTY); 492 } 493 494 if (type == null || type.equals(DependencyHandler.SMART_PROXY)) { 495 SmartProxyFactory proxyFactory = new SmartProxyFactory(this.getClass().getClassLoader()); 496 m_proxyObject = proxyFactory.getProxy(this); 497 } else { 498 DynamicProxyFactory proxyFactory = new DynamicProxyFactory(); 499 m_proxyObject = proxyFactory.getProxy(getSpecification()); 500 } 501 } else { 502 m_handler.warn("Cannot create a proxy for a service dependency which is not an interface " + 503 "- disabling proxy for " + getId()); 504 } 505 } 506 } 507 508 super.start(); 509 510 // Once the dependency is started, access to fields must be protected. 511 acquireWriteLockIfNotHeld(); 512 if (getBindingPolicy() == STATIC_BINDING_POLICY && m_handler.getInstanceManager().getPojoObjects() != null) { 513 m_isFrozen = true; 514 } 515 m_isStarted = true; 516 releaseWriteLockIfHeld(); 517 } 518 519 protected DependencyCallback[] getCallbacks() { 520 return m_callbacks; 521 } 522 523 /** 524 * Set that this dependency is a service level dependency. 525 * This forces the scoping policy to be STRICT. 526 */ 527 public void setServiceLevelDependency() { 528 m_isServiceLevelRequirement = true; 529 setBundleContext(new PolicyServiceContext(m_handler.getInstanceManager().getGlobalContext(), m_handler.getInstanceManager().getLocalServiceContext(), PolicyServiceContext.LOCAL)); 530 } 531 532 public String getId() { 533 // No synchronization required, the id is constant. 534 return m_id; 535 } 536 537 public boolean isServiceLevelRequirement() { 538 return m_isServiceLevelRequirement; 539 } 540 541 /** 542 * A new service has to be injected. 543 * 544 * @param reference : the new matching service reference. 545 * @see org.apache.felix.ipojo.util.DependencyModel#onServiceArrival(org.osgi.framework.ServiceReference) 546 */ 547 public void onServiceArrival(ServiceReference reference) { 548 callBindMethod(reference); 549 //The method is only called when a new service arrives, or when the used one is replaced. 550 } 551 552 /** 553 * An already injected service is modified. 554 * 555 * @param reference : the modified service reference. 556 * @see org.apache.felix.ipojo.util.DependencyModel#onServiceModification(org.osgi.framework.ServiceReference) 557 */ 558 public void onServiceModification(ServiceReference reference) { 559 callModifyMethod(reference); 560 } 561 562 /** 563 * A used (already injected) service disappears. 564 * 565 * @param ref : leaving service reference. 566 * @see org.apache.felix.ipojo.util.DependencyModel#onServiceDeparture(org.osgi.framework.ServiceReference) 567 */ 568 public void onServiceDeparture(ServiceReference ref) { 569 callUnbindMethod(ref); 570 } 571 572 /** 573 * The dependency has been reconfigured. 574 * Call unbind method and then bind methods. If the dependency cache is not reset, 575 * the thread continues to get older services. 576 * 577 * @param departs : no more matching services. 578 * @param arrivals : new services 579 * @see org.apache.felix.ipojo.util.DependencyModel#onDependencyReconfiguration(org.osgi.framework.ServiceReference[], org.osgi.framework.ServiceReference[]) 580 */ 581 public void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals) { 582 for (int i = 0; departs != null && i < departs.length; i++) { 583 callUnbindMethod(departs[i]); 584 } 585 586 for (int i = 0; arrivals != null && i < arrivals.length; i++) { 587 callBindMethod(arrivals[i]); 588 } 589 } 590 591 /** 592 * Reset the thread local cache if used. 593 * For testing purpose only. 594 */ 595 public void resetLocalCache() { 596 if (m_usage != null) { 597 Usage usage = (Usage) m_usage.get(); 598 if (usage.m_stack > 0) { 599 createServiceObject(usage); 600 } 601 } 602 } 603 604 /** 605 * Get the used service references list. 606 * 607 * @return the used service reference or null if no service reference are available. 608 */ 609 public List<ServiceReference> getServiceReferencesAsList() { 610 ServiceReference[] refs = super.getServiceReferences(); 611 if (refs == null) { 612 return null; 613 } else { 614 return Arrays.asList(refs); 615 } 616 } 617 618 619 /** 620 * Gets the list of callbacks attached to the current dependency. 621 * @return the array of dependency callback, {@code null} if no callbacks are attached to the current dependency. 622 */ 623 public DependencyCallback[] getDependencyCallbacks() { 624 return m_callbacks; 625 } 626 627 628 /** 629 * Called by the proxy to get service objects to delegate a method. 630 * On aggregate dependencies, it returns a list. 631 * 632 * @return a service object or a nullable/default-implementation object. 633 * For aggregate dependencies it returns a list or an empty list. 634 */ 635 public Object getService() { 636 // Check that we're in proxy mode. 637 if (!m_isProxy) { 638 throw new IllegalStateException("The dependency has not enabled the `proxy` mode."); 639 } 640 641 Usage usage = (Usage) m_usage.get(); 642 if (usage.m_stack == 0) { // uninitialized usage. 643 if (usage.m_componentStack > 0) { 644 // We comes from the component who didn't touch the service. 645 // So we initialize the usage. 646 createServiceObject(usage); 647 usage.inc(); // Start the caching, so set the stack level to 1 648 m_usage.set(usage); // Required by Dalvik. 649 if (isAggregate()) { 650 Object obj = usage.m_object; 651 if (obj instanceof Set) { 652 List<Object> list = new ArrayList<Object>(); 653 list.addAll((Set) obj); 654 return list; 655 } else { 656 // We already have a list 657 return obj; 658 } 659 } else { 660 return usage.m_object; 661 } 662 } else { 663 // External access => Immediate get. 664 if (isAggregate()) { 665 ServiceReference[] refs = getServiceReferences(); 666 if (refs == null) { 667 return new ArrayList(0); // Create an empty list. 668 } else { 669 List<Object> objs = new ArrayList<Object>(refs.length); 670 for (ServiceReference ref : refs) { 671 objs.add(getService(ref)); 672 } 673 return objs; 674 } 675 } else { // Scalar dependency. 676 ServiceReference ref = getServiceReference(); 677 if (ref != null) { 678 return getService(ref); 679 } else { 680 // No service available. 681 // TODO Decide what we have to do. 682 throw new RuntimeException("Service " + getSpecification() + " unavailable"); 683 } 684 } 685 } 686 } else { 687 // Use the copy. 688 // if the copy is a set, transform to a list 689 if (isAggregate()) { 690 Object obj = usage.m_object; 691 if (obj instanceof Set) { 692 List<Object> list = new ArrayList<Object>(); 693 list.addAll((Set) obj); 694 return list; 695 } else { 696 // We already have a list 697 return obj; 698 } 699 } else { 700 return usage.m_object; 701 } 702 703 } 704 } 705 706 /** 707 * This method is called by the replaced code in the component 708 * implementation class. Construct the service object list is necessary. 709 * 710 * @param pojo : POJO object. 711 * @param fieldName : field 712 * @param value : last value. 713 * @return the service object or a nullable / default implementation if defined. 714 * @see org.apache.felix.ipojo.FieldInterceptor#onGet(java.lang.Object, java.lang.String, java.lang.Object) 715 */ 716 public Object onGet(Object pojo, String fieldName, Object value) { 717 718 // Initialize the thread local object is not already touched. 719 Usage usage = m_usage.get(); 720 if (usage.m_stack == 0) { // uninitialized usage. 721 createServiceObject(usage); 722 usage.inc(); // Start the caching, so set the stack level to 1 723 m_usage.set(usage); // Required by Dalvik 724 } 725 if (!m_isProxy) { 726 return usage.m_object; 727 } else { 728 return m_proxyObject; 729 } 730 731 } 732 733 /** 734 * Creates the object to store in the given Thread Local. 735 * This object will be injected inside the POJO field. 736 * 737 * @param usage : Thread Local to populate. 738 */ 739 private void createServiceObject(Usage usage) { 740 ServiceReference[] refs = getServiceReferences(); 741 742 // manage timeout 743 if (refs == null) { 744 waitForServiceUntilTimeout(); 745 } 746 747 refs = getServiceReferences(); 748 749 if (!isAggregate()) { 750 if (refs == null) { 751 if (m_exception != null) { 752 // Throw the exception. 753 throw createExceptionToThrow(); 754 } 755 756 if (m_nullable == null && m_supportNullable) { 757 m_handler.warn("[" + m_handler.getInstanceManager().getInstanceName() + "] The dependency is not optional, however no service object can be injected in " + m_field + " -> " + getSpecification().getName()); 758 createNullableObject(); 759 } 760 usage.m_object = m_nullable; // Add null if the Nullable pattern is disabled. 761 } else { 762 ServiceReference ref = getServiceReference(); 763 usage.m_object = getService(ref); 764 } 765 } else { 766 switch(m_type) { 767 case ARRAY: 768 try { 769 if (refs == null) { 770 usage.m_object = (Object[]) Array.newInstance(getSpecification(), 0); // Create an empty array. 771 } else { 772 // Use a reflective construction to avoid class cast exception. This method allows setting the component type. 773 Object[] objs = (Object[]) Array.newInstance(getSpecification(), refs.length); 774 for (int i = 0; i < refs.length; i++) { 775 ServiceReference ref = refs[i]; 776 objs[i] = getService(ref); 777 } 778 usage.m_object = objs; 779 } 780 } catch (ArrayStoreException e) { 781 throw new RuntimeException("Cannot create the array - Check that the bundle can access the service interface", e); 782 } 783 break; 784 case LIST: 785 if (refs == null) { 786 usage.m_object = Collections.emptyList(); 787 } else { 788 // Use a list to store service objects 789 List<Object> objs = new ArrayList<Object>(refs.length); 790 for (ServiceReference ref : refs) { 791 objs.add(getService(ref)); 792 } 793 usage.m_object = objs; 794 } 795 break; 796 case SET: 797 if (refs == null) { 798 usage.m_object = Collections.emptySet(); 799 } else { 800 // Use a vector to store service objects 801 Set<Object> objs = new HashSet<Object>(refs.length); 802 for (ServiceReference ref : refs) { 803 objs.add(getService(ref)); 804 } 805 usage.m_object = objs; 806 } 807 break; 808 case VECTOR: 809 if (refs == null) { 810 usage.m_object = new Vector(0); // Create an empty vector. 811 } else { 812 // Use a vector to store service objects 813 Vector<Object> objs = new Vector<Object>(refs.length); 814 for (ServiceReference ref : refs) { 815 objs.add(getService(ref)); 816 } 817 usage.m_object = objs; 818 } 819 break; 820 } 821 } 822 } 823 824 /** 825 * Waits a service providers. The wait stops when the timeout is reached. 826 */ 827 private void waitForServiceUntilTimeout() { 828 // Begin to wait ... 829 long enter = System.currentTimeMillis(); 830 boolean exhausted = false; 831 832 // We used a synchronized block here because we must hold the monitor lock during the 'wait' 833 synchronized (this) { 834 while (getServiceReference() == null && !exhausted) { 835 try { 836 wait(1); 837 } catch (InterruptedException e) { 838 // We was interrupted .... 839 } finally { 840 long end = System.currentTimeMillis(); 841 exhausted = (end - enter) > m_timeout; 842 } 843 } 844 } 845 // When this method exit, the check will be done... 846 } 847 848 /** 849 * The field was set. 850 * This method should not be call if the POJO is written correctly. 851 * 852 * @param pojo : POJO object 853 * @param fieldName : field name 854 * @param value : set value. 855 * @see org.apache.felix.ipojo.FieldInterceptor#onSet(java.lang.Object, java.lang.String, java.lang.Object) 856 */ 857 public void onSet(Object pojo, String fieldName, Object value) { 858 // Nothing to do. 859 } 860 861 /** 862 * A POJO method will be invoked. 863 * 864 * @param pojo : Pojo object 865 * @param method : called method 866 * @param args : arguments 867 * @see org.apache.felix.ipojo.MethodInterceptor#onEntry(java.lang.Object, java.lang.reflect.Member, java.lang.Object[]) 868 */ 869 public void onEntry(Object pojo, Member method, Object[] args) { 870 if (m_usage != null) { 871 Usage usage = m_usage.get(); 872 usage.incComponentStack(); // Increment the number of component access. 873 if (usage.m_stack > 0) { 874 usage.inc(); 875 m_usage.set(usage); // Set the Thread local as value has been modified 876 } 877 } 878 } 879 880 /** 881 * A POJO method has thrown an error. 882 * This method does nothing and wait for the finally. 883 * 884 * @param pojo : POJO object. 885 * @param method : Method object. 886 * @param throwable : thrown error 887 * @see org.apache.felix.ipojo.MethodInterceptor#onError(java.lang.Object, java.lang.reflect.Member, java.lang.Throwable) 888 */ 889 public void onError(Object pojo, Member method, Throwable throwable) { 890 // Nothing to do : wait onFinally 891 } 892 893 /** 894 * A POJO method has returned. 895 * 896 * @param pojo : POJO object. 897 * @param method : Method object. 898 * @param returnedObj : returned object (null for void method) 899 * @see org.apache.felix.ipojo.MethodInterceptor#onExit(java.lang.Object, java.lang.reflect.Member, java.lang.Object) 900 */ 901 public void onExit(Object pojo, Member method, Object returnedObj) { 902 // Nothing to do : wait onFinally 903 } 904 905 /** 906 * A POJO method is finished. 907 * 908 * @param pojo : POJO object. 909 * @param method : Method object. 910 * @see org.apache.felix.ipojo.MethodInterceptor#onFinally(java.lang.Object, java.lang.reflect.Member) 911 */ 912 public void onFinally(Object pojo, Member method) { 913 if (m_usage != null) { 914 Usage usage = m_usage.get(); 915 usage.decComponentStack(); 916 if (usage.m_stack > 0) { 917 if (usage.dec()) { 918 // Exit the method flow => Release all objects 919 usage.clear(); 920 // Also remove the thread local object. 921 m_usage.remove(); 922 } 923 } 924 } 925 } 926 927 /** 928 * Gets true if the dependency use Nullable objects. 929 * 930 * @return true if the dependency is optional and supports nullable objects. 931 */ 932 public boolean supportsNullable() { 933 return isOptional() 934 && ! isAggregate() 935 && m_supportNullable; 936 } 937 938 public String getDefaultImplementation() { 939 return m_di; 940 } 941 942 public boolean isProxy() { 943 return m_isProxy; 944 } 945 946 public void setProxy(boolean proxy) { 947 m_isProxy = proxy; 948 } 949 950 /** 951 * Set the type to inject. 952 * This method set the dependency as aggregate. 953 * 954 * @param type the type to inject. 955 */ 956 protected void setAggregateType(AggregateDependencyInjectionType type) { 957 setAggregate(true); 958 m_type = type; 959 } 960 961 /** 962 * Sets the dependency timeout. 963 * 964 * @param timeout the timeout in ms. 965 */ 966 public void setTimeout(int timeout) { 967 m_timeout = timeout; 968 } 969 970 /** 971 * Gets the constructor parameter. 972 * 973 * @return the index of the constructor parameter, 974 * or <code>-1</code> if not set. 975 */ 976 public int getConstructorParameterIndex() { 977 return m_index; 978 } 979 980 /** 981 * Gets the object to inject in the constructor parameter. 982 * 983 * @param index the index of the parameter 984 * @return the created proxy object 985 * @see org.apache.felix.ipojo.ConstructorInjector#getConstructorParameter(int) 986 */ 987 public Object getConstructorParameter(int index) { 988 if (m_index == index && m_proxyObject != null) { 989 return m_proxyObject; 990 } 991 return null; 992 } 993 994 /** 995 * Gets the type of the constructor parameter. 996 * 997 * @param index the parameter index 998 * @return the class of the object. For scalar dependency, it's the 999 * specification, for aggregate it depends of the container object: 1000 * {@link List} or {@link Set}. 1001 * @see org.apache.felix.ipojo.ConstructorInjector#getConstructorParameterType(int) 1002 */ 1003 public Class getConstructorParameterType(int index) { 1004 if (m_index == index && m_proxyObject != null) { 1005 if (isAggregate()) { 1006 switch (m_type) { 1007 case LIST: 1008 return List.class; 1009 case SET: 1010 return Set.class; 1011 default: 1012 return null; // Should never happen, it was checked before. 1013 } 1014 } else { 1015 return getSpecification(); 1016 } 1017 } else { 1018 return null; 1019 } 1020 } 1021 1022 public String getException() { 1023 return m_exception; 1024 } 1025 1026 public int getTimeout() { 1027 return m_timeout; 1028 } 1029 1030 public AggregateDependencyInjectionType getAggregateType() { 1031 return m_type; 1032 } 1033 1034 /** 1035 * Classloader for nullable objects. 1036 */ 1037 private static class NullableClassLoader extends ClassLoader { 1038 /** 1039 * Component classloader. 1040 */ 1041 private ClassLoader m_component; 1042 /** 1043 * Specification classloader. 1044 */ 1045 private ClassLoader m_specification; 1046 1047 /** 1048 * Creates a NullableClassLoader. 1049 * 1050 * @param cmp the component class loader. 1051 * @param spec the specification class loader. 1052 */ 1053 public NullableClassLoader(ClassLoader cmp, ClassLoader spec) { 1054 m_component = cmp; 1055 m_specification = spec; 1056 } 1057 1058 /** 1059 * Loads the given class. 1060 * This method uses the classloader of the component class 1061 * and (if not found) the specification classloader. 1062 * 1063 * @param name the class name 1064 * @return the class object 1065 * @throws ClassNotFoundException if the class is not found by the two classloaders. 1066 * @see java.lang.ClassLoader#loadClass(java.lang.String) 1067 */ 1068 public Class loadClass(String name) throws ClassNotFoundException { 1069 try { 1070 return m_component.loadClass(name); 1071 } catch (ClassNotFoundException e) { 1072 return m_specification.loadClass(name); 1073 } 1074 } 1075 1076 1077 } 1078 1079 /** 1080 * Creates smart proxy object for proxied scalar dependencies. 1081 */ 1082 private class SmartProxyFactory extends ClassLoader { 1083 1084 /** 1085 * Handler classloader, used to load the temporal dependency class. 1086 */ 1087 private ClassLoader m_handlerCL; 1088 1089 /** 1090 * Creates the proxy classloader. 1091 * 1092 * @param parent the handler classloader. 1093 */ 1094 public SmartProxyFactory(ClassLoader parent) { 1095 super(getHandler().getInstanceManager().getFactory().getBundleClassLoader()); 1096 m_handlerCL = parent; 1097 } 1098 1099 /** 1100 * Loads a proxy class generated for the given (interface) class. 1101 * 1102 * @param clazz the service specification to proxy 1103 * @return the Class object of the proxy. 1104 */ 1105 protected Class getProxyClass(Class clazz) { 1106 byte[] clz = ProxyGenerator.dumpProxy(clazz); // Generate the proxy. 1107 // Turn around the VM changes (FELIX-2716) about java.* classes. 1108 String cn = clazz.getName(); 1109 if (cn.startsWith("java.")) { 1110 cn = "$" + cn; 1111 } 1112 return defineClass(cn + "$$Proxy", clz, 0, clz.length); 1113 } 1114 1115 /** 1116 * Create a proxy object for the given specification. The proxy 1117 * uses the given dependency to get the service object. 1118 * 1119 * @param dep the dependency used to get the service 1120 * @return the proxy object. 1121 */ 1122 public Object getProxy(Dependency dep) { 1123 try { 1124 Class clazz = getProxyClass(getSpecification()); 1125 Constructor constructor = clazz.getConstructor( 1126 new Class[]{clazz.getClassLoader().loadClass(Dependency.class.getName())}); 1127 return constructor.newInstance(new Object[]{dep}); 1128 } catch (Throwable e) { 1129 m_handler.error("Cannot create the proxy object", e); 1130 m_handler.getInstanceManager().stop(); 1131 return null; 1132 } 1133 } 1134 1135 /** 1136 * Loads the given class. 1137 * This method uses the classloader of the specification class 1138 * or the handler class loader. 1139 * 1140 * @param name the class name 1141 * @return the class object 1142 * @throws ClassNotFoundException if the class is not found by the two classloaders. 1143 * @see java.lang.ClassLoader#loadClass(java.lang.String) 1144 */ 1145 public Class loadClass(String name) throws ClassNotFoundException { 1146 try { 1147 return getHandler().getInstanceManager().getContext().getBundle().loadClass(name); 1148 } catch (ClassNotFoundException e) { 1149 return m_handlerCL.loadClass(name); 1150 } 1151 } 1152 } 1153 1154 /** 1155 * Creates java dynamic proxy object for proxied scalar dependencies. 1156 */ 1157 private class DynamicProxyFactory implements InvocationHandler { 1158 1159 /** 1160 * HashCode method. 1161 */ 1162 private Method m_hashCodeMethod; 1163 /** 1164 * Equals method. 1165 */ 1166 private Method m_equalsMethod; 1167 /** 1168 * toStirng method. 1169 */ 1170 private Method m_toStringMethod; 1171 1172 /** 1173 * Creates a DynamicProxyFactory. 1174 */ 1175 public DynamicProxyFactory() { 1176 try { 1177 m_hashCodeMethod = Object.class.getMethod("hashCode", null); 1178 m_equalsMethod = Object.class 1179 .getMethod("equals", new Class[]{Object.class}); 1180 m_toStringMethod = Object.class.getMethod("toString", null); 1181 } catch (NoSuchMethodException e) { 1182 throw new NoSuchMethodError(e.getMessage()); 1183 } 1184 } 1185 1186 /** 1187 * Creates a proxy object for the given specification. The proxy 1188 * uses the given dependency to get the service object. 1189 * 1190 * @param spec the service specification (interface) 1191 * @return the proxy object. 1192 */ 1193 public Object getProxy(Class spec) { 1194 return java.lang.reflect.Proxy.newProxyInstance( 1195 getHandler().getInstanceManager().getClazz().getClassLoader(), 1196 new Class[]{spec}, 1197 this); 1198 } 1199 1200 /** 1201 * Invocation Handler delegating invocation on the 1202 * service object. 1203 * 1204 * @param proxy the proxy object 1205 * @param method the method 1206 * @param args the arguments 1207 * @return a proxy object. 1208 * @throws Exception if the invocation throws an exception 1209 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 1210 */ 1211 public Object invoke(Object proxy, Method method, Object[] args) throws Exception { 1212 Object svc = getService(); 1213 Class declaringClass = method.getDeclaringClass(); 1214 if (declaringClass == Object.class) { 1215 if (method.equals(m_hashCodeMethod)) { 1216 return this.hashCode(); 1217 } else if (method.equals(m_equalsMethod)) { 1218 return proxy == args[0] ? Boolean.TRUE : Boolean.FALSE; 1219 } else if (method.equals(m_toStringMethod)) { 1220 return this.toString(); 1221 } else { 1222 throw new InternalError( 1223 "Unexpected Object method dispatched: " + method); 1224 } 1225 } 1226 1227 return method.invoke(svc, args); 1228 } 1229 1230 } 1231 1232}