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}