You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cl...@apache.org on 2007/09/25 15:27:54 UTC

svn commit: r579239 [8/8] - in /felix/trunk/ipojo: annotations/ ant/ arch/ arch/src/main/java/org/apache/felix/ipojo/arch/ arch/src/main/resources/ core/ core/src/main/java/org/apache/felix/ipojo/ core/src/main/java/org/apache/felix/ipojo/architecture/...

Added: felix/trunk/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java?rev=579239&view=auto
==============================================================================
--- felix/trunk/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java (added)
+++ felix/trunk/ipojo/core/src/main/java/org/apache/felix/ipojo/util/Tracker.java Tue Sep 25 06:27:49 2007
@@ -0,0 +1,745 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.ipojo.util;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TreeMap;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+* Utility class close to the OSGi Service Tracker.
+* 
+* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+*/
+public class Tracker implements TrackerCustomizer {
+
+    /**
+     * Bundle context against which this Tracker object is tracking.
+     */
+    protected final BundleContext m_context;
+
+    /**
+     * Filter specifying search criteria for the services to track.
+     */
+    protected Filter m_filter;
+
+    /**
+     * TrackerCustomizer object for this tracker.
+     */
+    final TrackerCustomizer m_customizer;
+
+    /**
+     * Filter string for use when adding the ServiceListener. If this field is set, then certain optimizations can be taken since we don't have a user supplied filter.
+     */
+    final String m_listenerFilter;
+
+    /**
+     * Class name to be tracked. If this field is set, then we are tracking by class name.
+     */
+    private final String m_trackClass;
+
+    /**
+     * Reference to be tracked. If this field is set, then we are tracking a single ServiceReference.
+     */
+    private final ServiceReference m_trackReference;
+
+    /**
+     * Tracked services: ServiceReference object -> customized. Object and ServiceListener object
+     */
+    private Tracked m_tracked;
+
+    /**
+     * Cached ServiceReference for getServiceReference. This field is volatile since it is accessed by multiple threads.
+     */
+    private volatile ServiceReference m_cachedReference;
+
+    /**
+     * Cached service object for getService. This field is volatile since it is accessed by multiple threads.
+     */
+    private volatile Object m_cachedService;
+
+    /**
+     * Create a Tracker object on the specified ServiceReference object.
+     * The service referenced by the specified ServiceReference object will be tracked by this Tracker.
+     * @param context BundleContext object against which the tracking is done.
+     * @param reference ServiceReference object for the service to be tracked.
+     * @param customizer The customizer object to call when services are added, modified, or removed in this Tracker object. If customizer is null, then this Tracker object will be used as
+     *            the TrackerCustomizer object and the Tracker object will call the TrackerCustomizer methods on itself.
+     */
+    public Tracker(BundleContext context, ServiceReference reference, TrackerCustomizer customizer) {
+        m_context = context;
+        m_trackReference = reference;
+        m_trackClass = null;
+        if (customizer == null) {
+            m_customizer = this;
+        } else {
+            m_customizer = customizer;
+        }
+        m_listenerFilter = "(" + Constants.SERVICE_ID + "=" + reference.getProperty(Constants.SERVICE_ID).toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        try {
+            this.m_filter = context.createFilter(m_listenerFilter);
+        } catch (InvalidSyntaxException e) { // we could only get this exception if the ServiceReference was invalid
+            throw new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Create a Tracker object on the specified class name.
+     * Services registered under the specified class name will be tracked by this Tracker object.
+     * @param context BundleContext object against which the tracking is done.
+     * @param clazz Class name of the services to be tracked.
+     * @param customizer The customizer object to call when services are added, modified, or removed in this Tracker object. If customizer is null, then this Tracker object will be used as
+     *            the TrackerCustomizer object and the Tracker object will call the TrackerCustomizer methods on itself.
+     */
+    public Tracker(BundleContext context, String clazz, TrackerCustomizer customizer) {
+        this.m_context = context;
+        this.m_trackReference = null;
+        this.m_trackClass = clazz;
+        if (customizer == null) {
+            m_customizer = this;
+        } else {
+            m_customizer = customizer;
+        }
+        this.m_listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        try {
+            this.m_filter = context.createFilter(m_listenerFilter);
+        } catch (InvalidSyntaxException e) { // we could only get this exception
+            // if the clazz argument was
+            // malformed
+            throw new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Create a Tracker object on the specified Filter object.
+     * <p>
+     * Services which match the specified Filter object will be tracked by this Tracker object.
+     * @param context BundleContext object against which the tracking is done.
+     * @param filter Filter object to select the services to be tracked.
+     * @param customizer The customizer object to call when services are added, modified, or removed in this Tracker object. If customizer is null, then this Tracker object will be used as the
+     *            TrackerCustomizer object and the Tracker object will call the TrackerCustomizer methods on itself.
+     * @since 1.1
+     */
+    public Tracker(BundleContext context, Filter filter, TrackerCustomizer customizer) {
+        this.m_context = context;
+        this.m_trackReference = null;
+        this.m_trackClass = null;
+        this.m_listenerFilter = null;
+        this.m_filter = filter;
+        if (customizer == null) {
+            m_customizer = this;
+        } else {
+            m_customizer = customizer;
+        }
+        if ((context == null) || (filter == null)) { // we throw a NPE here to be consistent with the other constructors
+            throw new NullPointerException();
+        }
+    }
+
+    /**
+     * Open this Tracker object and begin tracking services.
+     * <p>
+     * Services which match the search criteria specified when this Tracker object was created are now tracked by this Tracker object.
+     */
+    public synchronized void open() {
+        if (m_tracked != null) { return; }
+
+        m_tracked = new Tracked();
+        synchronized (m_tracked) {
+            try {
+                m_context.addServiceListener(m_tracked, m_listenerFilter);
+                ServiceReference[] references;
+                if (m_listenerFilter == null) { // user supplied filter
+                    references = getInitialReferences(null, m_filter.toString());
+                } else { // constructor supplied filter
+                    if (m_trackClass == null) {
+                        references = new ServiceReference[] { m_trackReference };
+                    } else {
+                        references = getInitialReferences(m_trackClass, null);
+                    }
+                }
+
+                m_tracked.setInitialServices(references); // set tracked with
+                // the initial
+                // references
+            } catch (InvalidSyntaxException e) {
+                throw new RuntimeException("unexpected InvalidSyntaxException: " + e.getMessage()); //$NON-NLS-1$
+            }
+        }
+        /* Call tracked outside of synchronized region */
+        m_tracked.trackInitialServices(); // process the initial references
+    }
+
+    /**
+     * Returns the list of initial ServiceReference objects that will be tracked by this Tracker object.
+     * @param trackClass the class name with which the service was registered, or null for all services.
+     * @param filterString the filter criteria or null for all services.
+     * @return the list of initial ServiceReference objects.
+     * @throws InvalidSyntaxException if the filter uses an invalid syntax.
+     */
+    private ServiceReference[] getInitialReferences(String trackClass, String filterString) throws InvalidSyntaxException {
+        return m_context.getServiceReferences(trackClass, filterString);
+    }
+
+    /**
+     * Close this Tracker object.
+     * <p>
+     * This method should be called when this Tracker object should end the tracking of services.
+     */
+    public synchronized void close() {
+        if (m_tracked == null) { return; }
+
+        m_tracked.close();
+        ServiceReference[] references = getServiceReferences();
+        Tracked outgoing = m_tracked;
+        m_tracked = null;
+        try {
+            m_context.removeServiceListener(outgoing);
+        } catch (IllegalStateException e) {
+            System.err.println("Context stopped");
+            /* In case the context was stopped. */
+        }
+        if (references != null) {
+            for (int i = 0; i < references.length; i++) {
+                outgoing.untrack(references[i]);
+            }
+        }
+
+    }
+
+    /**
+     * Default implementation of the TrackerCustomizer.addingService method.
+     * <p>
+     * This method is only called when this Tracker object has been constructed with a null TrackerCustomizer argument. The default implementation returns the result of calling getService,
+     * on the BundleContext object with which this Tracker object was created, passing the specified ServiceReference object.
+     * <p>
+     * This method can be overridden in a subclass to customize the service object to be tracked for the service being added. In that case, take care not to rely on the default implementation of removedService that will unget the service.
+     * @param reference Reference to service being added to this Tracker object.
+     * @return The service object to be tracked for the service added to this Tracker object.
+     * @see TrackerCustomizer
+     */
+    public boolean addingService(ServiceReference reference) {
+        return true;
+    }
+
+    /**
+     * Default implementation of the TrackerCustomizer.modifiedService method.
+     * <p>
+     * This method is only called when this Tracker object has been constructed with a null TrackerCustomizer argument. The default implementation does nothing.
+     * @param reference Reference to modified service.
+     * @param service The service object for the modified service.
+     * @see TrackerCustomizer
+     */
+    public void modifiedService(ServiceReference reference, Object service) {
+    }
+
+    /**
+     * Default implementation of the TrackerCustomizer.removedService method.
+     * <p>
+     * This method is only called when this Tracker object has been constructed with a null TrackerCustomizer argument. The default implementation calls ungetService, on the
+     * BundleContext object with which this Tracker object was created, passing the specified ServiceReference object.
+     * <p>
+     * This method can be overridden in a subclass. If the default implementation of addingService method was used, this method must unget the service.
+     * @param reference Reference to removed service.
+     * @param service The service object for the removed service.
+     * @see TrackerCustomizer
+     */
+    public void removedService(ServiceReference reference, Object service) {
+        m_context.ungetService(reference);
+    }
+
+    /**
+     * Wait for at least one service to be tracked by this Tracker object.
+     * <p>
+     * It is strongly recommended that waitForService is not used during the calling of the BundleActivator methods. BundleActivator methods are expected to complete in a short period of time.
+     * @param timeout time interval in milliseconds to wait. If zero, the method will wait indefinately.
+     * @return Returns the result of getService().
+     * @throws InterruptedException If another thread has interrupted the current thread.
+     */
+    public Object waitForService(long timeout) throws InterruptedException {
+        if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); }
+        Object object = getService();
+        while (object == null) {
+            Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+            if (tracked == null) { /* if Tracker is not open */
+                return null;
+            }
+            synchronized (tracked) {
+                if (tracked.size() == 0) {
+                    tracked.wait(timeout);
+                }
+            }
+            object = getService();
+            if (timeout > 0) { return object; }
+        }
+        return object;
+    }
+
+    /**
+     * Return an array of ServiceReference objects for all services being tracked by this Tracker object.
+     * @return Array of ServiceReference objects or null if no service are being tracked.
+     */
+    public ServiceReference[] getServiceReferences() {
+        Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+        if (tracked == null) { // if Tracker is not open
+            return null;
+        }
+        synchronized (tracked) {
+            int length = tracked.size();
+            if (length == 0) { return null; }
+            ServiceReference[] references = new ServiceReference[length];
+            Iterator keys = tracked.keySet().iterator();
+            for (int i = 0; i < length; i++) {
+                references[i] = (ServiceReference) keys.next();
+            }
+            // The resulting array is sorted by ranking.
+            return references;
+        }
+    }
+    
+    /**
+     * Get the list of stored service reference.
+     * @return the list containing used service reference
+     */
+    public List/*<ServiceReference>*/ getServiceReferencesList() {
+        Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+        if (tracked == null) { // if Tracker is not open
+            return null;
+        }
+        synchronized (tracked) {
+            int length = tracked.size();
+            if (length == 0) { return null; }
+            List references = new ArrayList(length);
+            Iterator keys = tracked.keySet().iterator();
+            for (int i = 0; i < length; i++) {
+                references.add(keys.next());
+            }
+            // The resulting array is sorted by ranking.
+            return references;
+        }
+    }
+
+    /**
+     * Returns a ServiceReference object for one of the services being tracked by this Tracker object.
+     * If multiple services are being tracked, the service with the highest ranking (as specified in its service.ranking property) is returned.
+     * If there is a tie in ranking, the service with the lowest service ID (as specified in its service.id property); that is, the service that was registered first is returned.
+     * This is the same algorithm used by BundleContext.getServiceReference.
+     * @return ServiceReference object or null if no service is being tracked.
+     * @since 1.1
+     */
+    public ServiceReference getServiceReference() {
+        ServiceReference reference = m_cachedReference;
+        if (reference != null) { return reference; }
+
+        ServiceReference[] references = getServiceReferences();
+        if (references == null) {
+            return null;
+        } else {
+            // As the map is sorted, return the first element.
+            return m_cachedReference = references[0];
+        }
+    }
+
+    /**
+     * Returns the service object for the specified ServiceReference object if the referenced service is being tracked by this Tracker object.
+     * @param reference Reference to the desired service.
+     * @return Service object or null if the service referenced by the specified ServiceReference object is not being tracked.
+     */
+    public Object getService(ServiceReference reference) {
+        Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+        if (tracked == null) { /* if Tracker is not open */
+            return null;
+        }
+        Object object = null;
+        synchronized (tracked) {
+            object =  tracked.get(reference);
+            if (object != null) { // The object was already get.
+                return object;
+            } else if (tracked.containsKey(reference)) { // Not already get
+                object = m_context.getService(reference);
+                tracked.put(reference, object);
+                return object;
+            }
+        }
+        // Not already tracked.
+        return m_context.getService(reference);
+    }
+    
+    /**
+     * Unget the given service reference.
+     * @param reference : service reference to unget.
+     */
+    public void ungetService(ServiceReference reference) {
+        Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+        if (tracked == null) { /* if Tracker is not open */
+            return;
+        }
+        Object object = null;
+        synchronized (tracked) {
+            object = tracked.get(reference);
+        }
+        if (object != null) { m_context.ungetService(reference); }
+    }
+
+    /**
+     * Return an array of service objects for all services being tracked by this Tracker object.
+     * @return Array of service objects or null if no service are being tracked.
+     */
+    public Object[] getServices() {
+        Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+        if (tracked == null) { /* if Tracker is not open */
+            return null;
+        }
+        synchronized (tracked) {
+            ServiceReference[] references = getServiceReferences();
+            int length = 0;
+            if (references != null) {
+                length = references.length;
+            } else {
+                return null; 
+            }
+            Object[] objects = new Object[length];
+            for (int i = 0; i < length; i++) {
+                objects[i] = getService(references[i]);
+            }
+            return objects;
+        }
+    }
+
+    /**
+     * Returns a service object for one of the services being tracked by this Tracker object.
+     * <p>
+     * If any services are being tracked, this method returns the result of calling getService(getServiceReference()).
+     * @return Service object or null if no service is being tracked.
+     */
+    public Object getService() {
+        Object service = m_cachedService;
+        if (service != null) { return service; }
+        ServiceReference reference = getServiceReference();
+        if (reference == null) { return null; }
+        return m_cachedService = getService(reference);
+    }
+
+    /**
+     * Remove a service from this Tracker object. The specified service will be removed from this Tracker object. If the specified service was being tracked then the
+     * TrackerCustomizer.removedService method will be called for that service.
+     * @param reference Reference to the service to be removed.
+     */
+    public void remove(ServiceReference reference) {
+        Tracked tracked = this.m_tracked; // use local var since we are not synchronized
+        if (tracked == null) { /* if Tracker is not open */
+            return;
+        }
+        tracked.untrack(reference);
+    }
+
+    /**
+     * Return the number of services being tracked by this Tracker object.
+     * @return Number of services being tracked.
+     */
+    public int size() {
+        Tracked tracked = this.m_tracked; //use local var since we are not synchronized
+        if (tracked == null) { /* if Tracker is not open */
+            return 0;
+        }
+        return tracked.size();
+    }
+
+    /**
+     * Inner class to track services. If a Tracker object is reused (closed then reopened), then a new Tracked object is used. This class is a hashtable mapping ServiceReference object -> customized Object. This
+     * class is the ServiceListener object for the tracker. This class is used to synchronize access to the tracked services. This is not a public class. It is only for use by the implementation of the Tracker
+     * class.
+     */
+    class Tracked extends TreeMap implements ServiceListener {
+        /**
+         * UID.
+         */
+        static final long serialVersionUID = -7420065199791006079L;
+    
+        /**
+         * List of ServiceReferences in the process of being added. This is used to deal with nesting of ServiceEvents. Since ServiceEvents are synchronously delivered, ServiceEvents can be nested. For example, when processing the adding of a service
+         * and the customizer causes the service to be unregistered, notification to the nested call to untrack that the service was unregistered can be made to the track method. Since the ArrayList implementation is not synchronized, all access to
+         * this list must be protected by the same synchronized object for thread safety.
+         */
+        private List m_adding;
+    
+        /**
+         * true if the tracked object is closed. This field is volatile because it is set by one thread and read by another.
+         */
+        private volatile boolean m_closed;
+    
+        /**
+         * Initial list of ServiceReferences for the tracker. This is used to correctly process the initial services which could become unregistered before they are tracked. This is necessary since the initial set of tracked services are not
+         * "announced" by ServiceEvents and therefore the ServiceEvent for unregistration could be delivered before we track the service. A service must not be in both the initial and adding lists at the same time. A service must be moved from the
+         * initial list to the adding list "atomically" before we begin tracking it. Since the LinkedList implementation is not synchronized, all access to this list must be protected by the same synchronized object for thread safety.
+         */
+        private List m_initial;
+    
+        /**
+         * Tracked constructor.
+         */
+        protected Tracked() {
+            super(new ReferenceComparator());
+            m_closed = false;
+            m_adding = new ArrayList(6);
+            m_initial = new LinkedList();
+        }
+    
+        /**
+         * Set initial list of services into tracker before ServiceEvents begin to be received. This method must be called from Tracker.open while synchronized on this object in the same synchronized block as the addServiceListener call.
+         * @param references The initial list of services to be tracked.
+         */
+        protected void setInitialServices(ServiceReference[] references) {
+            if (references == null) { return; }
+            int size = references.length;
+            for (int i = 0; i < size; i++) {
+                m_initial.add(references[i]);
+            }
+        }
+    
+        /**
+         * Track the initial list of services. This is called after ServiceEvents can begin to be received. This method must be called from Tracker.open while not synchronized on this object after the addServiceListener call.
+         */
+        protected void trackInitialServices() {
+            while (true) {
+                ServiceReference reference;
+                synchronized (this) {
+                    if (m_initial.size() == 0) { //  if there are no more inital services
+                        return; // we are done
+                    }
+    
+                    // move the first service from the initial list to the adding list within this synchronized block.
+                    reference = (ServiceReference) ((LinkedList) m_initial).removeFirst();
+                    if (this.containsKey(reference)) { //Check if the reference is already tracked.
+                        //if we are already tracking this service
+                        continue; /* skip this service */
+                    }
+                    if (m_adding.contains(reference)) {
+                        // if this service is already in the process of being added.
+                        continue; // skip this service
+                    }
+                    m_adding.add(reference);
+                }
+                trackAdding(reference); // Begin tracking it. We call trackAdding since we have already put the reference in the adding list.
+            }
+        }
+    
+        /**
+         * Called by the owning Tracker object when it is closed.
+         */
+        protected void close() {
+            m_closed = true;
+        }
+    
+        /**
+         * ServiceListener method for the Tracker class. This method must NOT be synchronized to avoid deadlock potential.
+         * @param event ServiceEvent object from the framework.
+         */
+        public void serviceChanged(ServiceEvent event) {
+            //Check if we had a delayed call (which could happen when we close).
+            if (m_closed) { return; }
+            ServiceReference reference = event.getServiceReference();
+    
+            switch (event.getType()) {
+                case ServiceEvent.REGISTERED:
+                case ServiceEvent.MODIFIED:
+                    if (m_listenerFilter != null) { // constructor supplied filter
+                        track(reference);
+                    } else { // user supplied filter
+                        if (m_filter.match(reference)) {
+                            track(reference); // Arrival
+                        } else {
+                            untrack(reference); // Departure
+                        }
+                    }
+                    break;
+                case ServiceEvent.UNREGISTERING:
+                    untrack(reference); // Departure
+                    break;
+                default:
+                    break;
+            }
+        }
+    
+        /**
+         * Begin to track the referenced service.
+         * @param reference Reference to a service to be tracked.
+         */
+        protected void track(ServiceReference reference) {
+            Object object;
+            boolean alreadyTracked;
+            synchronized (this) {
+                alreadyTracked = this.containsKey(reference);
+                object = this.get(reference);
+            }
+            if (alreadyTracked) { // we are already tracking the service
+                if (object != null) { // If already get, invalidate the cache
+                    synchronized (this) {
+                        modified();
+                    }
+                }
+                // Call customizer outside of synchronized region
+                m_customizer.modifiedService(reference, object);
+                return;
+            }
+            synchronized (this) {
+                if (m_adding.contains(reference)) { // if this service is already in the process of being added.
+                    return;
+                }
+                m_adding.add(reference); // mark this service is being added
+            }
+    
+            trackAdding(reference); // call trackAdding now that we have put the reference in the adding list
+        }
+    
+        /**
+         * Common logic to add a service to the tracker used by track and trackInitialServices. The specified reference must have been placed in the adding list before calling this method.
+         * @param reference Reference to a service to be tracked.
+         */
+        private void trackAdding(ServiceReference reference) {
+            boolean mustBeTracked = false;
+            boolean becameUntracked = false;
+            //Call customizer outside of synchronized region
+            try {
+                mustBeTracked = m_customizer.addingService(reference);
+            } finally {
+                synchronized (this) {
+                    if (m_adding.remove(reference)) { // if the service was not untracked during the customizer callback
+                        if (mustBeTracked) {
+                            this.put(reference, null);
+                            modified();
+                            notifyAll(); // notify any waiters in waitForService
+                        }
+                    } else {
+                        becameUntracked = true;
+                    }
+                }
+            }
+            /*
+             * The service became untracked during the customizer callback.
+             */
+            if (becameUntracked) {
+                /* Call customizer outside of synchronized region */
+                m_customizer.removedService(reference, null);
+            }
+        }
+    
+        /**
+         * Discontinue tracking the referenced service.
+         * @param reference Reference to the tracked service.
+         */
+        protected void untrack(ServiceReference reference) {
+            Object object;
+            synchronized (this) {
+                if (m_initial.remove(reference)) { // if this service is already in the list of initial references to process
+                    return; // we have removed it from the list and it will not be processed
+                }
+    
+                if (m_adding.remove(reference)) { // if the service is in the process of being added
+                    return; // in case the service is untracked while in the process of adding
+                }
+                
+                boolean isTraked = this.containsKey(reference); // Check if we was tracking the reference 
+                object = this.remove(reference); // must remove from tracker before calling customizer callback
+                 
+                if (! isTraked) { return; }
+                modified();
+            }
+            // Call customizer outside of synchronized region
+            m_customizer.removedService(reference, object);
+            // If the customizer throws an unchecked exception, it is safe to let it propagate
+        }
+
+        /**
+         * Called by the Tracked object whenever the set of tracked services is modified. Increments the tracking count and clears the cache.
+         * This method must not be synchronized since it is called by Tracked while Tracked is synchronized. We don't want synchronization interactions between the ServiceListener thread and the user thread.
+         */
+        void modified() {
+            m_cachedReference = null; /* clear cached value */
+            m_cachedService = null; /* clear cached value */
+        }
+        
+    }
+
+    /**
+     *Service Reference Comparator.
+     */
+    private class ReferenceComparator implements Comparator {
+
+        /**
+         * Compare two service reference.
+         * @param ref1 : reference 1
+         * @param ref2 : reference 2
+         * @return -1 if the reference 1 is 'higher' than the reference 2, 1 otherwise. (higher is term of ranking means a lower index)
+         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+         */
+        public int compare(Object ref1, Object ref2) {
+            if (ref1.equals(ref2)) {
+                return 0;
+            }
+            
+            if (ref1 instanceof ServiceReference && ref2 instanceof ServiceReference) {                
+                Object property1 = ((ServiceReference) ref1).getProperty(Constants.SERVICE_RANKING);
+                Object property2 = ((ServiceReference) ref2).getProperty(Constants.SERVICE_RANKING);
+                
+                int rank1 = 0;
+                int rank2 = 0;
+                if (property1 instanceof Integer) { rank1 = ((Integer) property1).intValue(); }
+                if (property2 instanceof Integer) { rank2 = ((Integer) property2).intValue(); }
+                
+                if (rank1 == rank2) {
+                    // Check service.id
+                    Object sid1 = ((ServiceReference) ref1).getProperty(Constants.SERVICE_ID);
+                    Object sid2 = ((ServiceReference) ref2).getProperty(Constants.SERVICE_ID);
+                    
+                    long rankId1 = ((Long) sid1).longValue();
+                    long rankId2 = ((Long) sid2).longValue();
+                    
+                    if (rankId1 == rankId2) {
+                        return 0;
+                    } else if (rankId1 < rankId2) {
+                        return -1;
+                    } else {
+                        return 1;
+                    }
+                    
+                } else if (rank1 > rank2) {
+                    return 1;
+                } else {
+                    return -1;
+                }
+                
+            } else {
+                return 0;
+            }
+        }
+    }
+
+}

Added: felix/trunk/ipojo/core/src/main/java/org/apache/felix/ipojo/util/TrackerCustomizer.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/core/src/main/java/org/apache/felix/ipojo/util/TrackerCustomizer.java?rev=579239&view=auto
==============================================================================
--- felix/trunk/ipojo/core/src/main/java/org/apache/felix/ipojo/util/TrackerCustomizer.java (added)
+++ felix/trunk/ipojo/core/src/main/java/org/apache/felix/ipojo/util/TrackerCustomizer.java Tue Sep 25 06:27:49 2007
@@ -0,0 +1,54 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.ipojo.util;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Tracker Customizer.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public interface TrackerCustomizer {
+
+    /**
+     * A service is being added to the Tracker object.
+     * This method is called before a service which matched the search parameters of the Tracker object is added to it. This method should return the service object to be tracked for this ServiceReference object.
+     * The returned service object is stored in the Tracker object and is available from the getService and getServices methods.
+     * @param reference Reference to service being added to the Tracker object.
+     * @return The service object to be tracked for the ServiceReference object or null if the ServiceReference object should not be tracked.
+     */
+    boolean addingService(ServiceReference reference);
+
+    /**
+     * A service tracked by the Tracker object has been modified.
+     * This method is called when a service being tracked by the Tracker object has had it properties modified.
+     * @param reference Reference to service that has been modified.
+     * @param service The service object for the modified service.
+     */
+    void modifiedService(ServiceReference reference, Object service);
+
+    /**
+     * A service tracked by the Tracker object has been removed.
+     * This method is called after a service is no longer being tracked by the Tracker object.
+     * @param reference Reference to service that has been removed.
+     * @param service The service object for the removed service.
+     */
+    void removedService(ServiceReference reference, Object service);
+
+}

Added: felix/trunk/ipojo/core/src/main/resources/metadata.xml
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/core/src/main/resources/metadata.xml?rev=579239&view=auto
==============================================================================
--- felix/trunk/ipojo/core/src/main/resources/metadata.xml (added)
+++ felix/trunk/ipojo/core/src/main/resources/metadata.xml Tue Sep 25 06:27:49 2007
@@ -0,0 +1,41 @@
+<ipojo>
+<!-- Primitives handler -->
+<handler classname="org.apache.felix.ipojo.handlers.lifecycle.controller.ControllerHandler" name="controller"/>
+<handler classname="org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallbackHandler" name="callback"/>
+<handler classname="org.apache.felix.ipojo.handlers.dependency.DependencyHandler" name="requires">
+	<controller field="m_state"/>
+</handler>
+<handler classname="org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandler" name="provides"/>
+<handler classname="org.apache.felix.ipojo.handlers.configuration.ConfigurationHandler" name="properties"/>
+<handler classname="org.apache.felix.ipojo.handlers.architecture.ArchitectureHandler" name="architecture">
+	<provides>
+		<property field="m_name" name="instance.name" value=""/>
+	</provides>
+</handler>
+
+<!-- Composite Handler -->
+<handler classname="org.apache.felix.ipojo.composite.instance.InstanceHandler" name="instance" type="composite">
+	<controller field="m_isValid"/>
+	<requires filter="(factory.state=1)" field="m_factories" optional="true">
+		<callback type="bind" method="bindFactory"/>
+		<callback type="unbind" method="unbindFactory"/>
+	</requires>
+</handler>
+<handler classname="org.apache.felix.ipojo.composite.service.importer.ImportHandler" name="requires" type="composite">
+	<controller field="m_valid"/>
+</handler>
+<handler classname="org.apache.felix.ipojo.composite.service.importer.ExportHandler" name="exports" type="composite">
+	<controller field="m_valid"/>
+</handler>
+<handler classname="org.apache.felix.ipojo.composite.service.instantiator.ServiceInstantiatorHandler" name="service" type="composite">
+	<controller field="m_isValid"/>
+</handler>
+<handler classname="org.apache.felix.ipojo.composite.service.provides.ProvidedServiceHandler" name="provides" type="composite">
+	<controller field="m_valid"/>
+</handler>
+<handler classname="org.apache.felix.ipojo.composite.architecture.ArchitectureHandler" name="architecture" type="composite">
+	<provides>
+		<property field="m_name" name="instance.name" value=""/>
+	</provides>
+</handler>
+</ipojo>
\ No newline at end of file

Modified: felix/trunk/ipojo/manipulator/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/pom.xml?rev=579239&r1=579238&r2=579239&view=diff
==============================================================================
--- felix/trunk/ipojo/manipulator/pom.xml (original)
+++ felix/trunk/ipojo/manipulator/pom.xml Tue Sep 25 06:27:49 2007
@@ -9,8 +9,10 @@
   <modelVersion>4.0.0</modelVersion>
   <packaging>bundle</packaging>
   <artifactId>org.apache.felix.ipojo.manipulator</artifactId>
-  <version>0.7.3-SNAPSHOT</version>
-  <name>Apache Felix iPOJO Manipulator</name>
+  <version>0.7.5-SNAPSHOT</version>
+  <name>Apache Felix iPOJO Manipulator</name>
+  
+  
   <dependencies>
     <dependency>
       <groupId>asm</groupId>
@@ -20,18 +22,18 @@
     <dependency>
       <groupId>asm</groupId>
       <artifactId>asm-commons</artifactId>
-      <version>3.0</version>
-      <exclusions>
-    	<exclusion>
-      		<groupId>asm</groupId>
-      		<artifactId>asm-tree</artifactId>
-    	</exclusion>
+      <version>3.0</version>
+      <exclusions>
+    	<exclusion>
+      		<groupId>asm</groupId>
+      		<artifactId>asm-tree</artifactId>
+    	</exclusion>
   	   </exclusions>
     </dependency>
     <dependency>
       <groupId>${pom.groupId}</groupId>
       <artifactId>org.apache.felix.ipojo.metadata</artifactId>
-      <version>0.7.3-SNAPSHOT</version>
+      <version>0.7.5-SNAPSHOT</version>
     </dependency>
   </dependencies>
   <build>

Modified: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ManipulationProperty.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ManipulationProperty.java?rev=579239&r1=579238&r2=579239&view=diff
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ManipulationProperty.java (original)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ManipulationProperty.java Tue Sep 25 06:27:49 2007
@@ -60,15 +60,15 @@
      * Helper array for byte code manipulation of primitive type.
      */
     protected static final String[][] PRIMITIVE_BOXING_INFORMATION = new String[][] { 
-        { "V", "ILLEGAL", "ILLEGAL" }, 
-        { "Z", "java/lang/Boolean", "booleanValue" },
-        { "C", "java/lang/Character", "charValue" }, 
-        { "B", "java/lang/Byte", "byteValue" }, 
-        { "S", "java/lang/Short", "shortValue" }, 
-        { "I", "java/lang/Integer", "intValue" },
-        { "F", "java/lang/Float", "floatValue" }, 
-        { "J", "java/lang/Long", "longValue" }, 
-        { "D", "java/lang/Double", "doubleValue" }
+        {"V", "ILLEGAL", "ILLEGAL"}, 
+        {"Z", "java/lang/Boolean", "booleanValue"},
+        {"C", "java/lang/Character", "charValue"}, 
+        {"B", "java/lang/Byte", "byteValue"}, 
+        {"S", "java/lang/Short", "shortValue"}, 
+        {"I", "java/lang/Integer", "intValue"},
+        {"F", "java/lang/Float", "floatValue"}, 
+        {"J", "java/lang/Long", "longValue"}, 
+        {"D", "java/lang/Double", "doubleValue"}
     };
     
     /**

Modified: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java?rev=579239&r1=579238&r2=579239&view=diff
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java (original)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/Manipulator.java Tue Sep 25 06:27:49 2007
@@ -141,6 +141,7 @@
                 ManipulationProperty.getLogger().log(ManipulationProperty.INFO, "Put the file " + clazz.getAbsolutePath() + " in the jar file");
             } catch (Exception e) {
                 System.err.println("Problem to write the adapted class on the file system " + " [ " + clazz.getAbsolutePath() + " ] " + e.getMessage());
+                e.printStackTrace();
             }
         }
         // The file is in the bundle

Modified: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/PojoAdapter.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/PojoAdapter.java?rev=579239&r1=579238&r2=579239&view=diff
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/PojoAdapter.java (original)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/PojoAdapter.java Tue Sep 25 06:27:49 2007
@@ -173,10 +173,8 @@
      */
     public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
         // Avoid manipulating special method
-		if (name.equals("<clinit>") || name.equals("class$")) {
-            return super.visitMethod(access, name, desc, signature, exceptions);
-        }
-		// The constructor is manipulated separatly
+        if (name.equals("<clinit>") || name.equals("class$")) { return super.visitMethod(access, name, desc, signature, exceptions); }
+        // The constructor is manipulated separatly
         if (name.equals("<init>")) {
             // 1) change the constructor descriptor (add a component manager arg as first argument)
             String newDesc = desc.substring(1);
@@ -188,16 +186,12 @@
             if (mv == null) {
                 return null;
             } else {
-                //return new ConstructorCodeAdapter(mv, access, desc, m_owner);
+                // return new ConstructorCodeAdapter(mv, access, desc, m_owner);
                 return new ConstructorCodeAdapter(mv, m_owner);
             }
         } else { // "Normal methods"
-
-			// avoid manipulating static methods.
-		  	if ((access & ACC_STATIC) == ACC_STATIC) {
-		  			return super.visitMethod(access, name, desc, signature, exceptions);
-            }
-			
+            // avoid manipulating static methods.
+            if ((access & ACC_STATIC) == ACC_STATIC) { return super.visitMethod(access, name, desc, signature, exceptions); }
             Type[] args = Type.getArgumentTypes(desc);
             String id = name;
             for (int i = 0; i < args.length; i++) {

Modified: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java?rev=579239&r1=579238&r2=579239&view=diff
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java (original)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulator/Pojoization.java Tue Sep 25 06:27:49 2007
@@ -40,6 +40,7 @@
 
 import org.apache.felix.ipojo.manipulation.Manipulator;
 import org.apache.felix.ipojo.manipulation.annotations.MetadataCollector;
+import org.apache.felix.ipojo.metadata.Attribute;
 import org.apache.felix.ipojo.metadata.Element;
 import org.apache.felix.ipojo.xml.parser.ParseException;
 import org.apache.felix.ipojo.xml.parser.XMLMetadataParser;
@@ -89,7 +90,6 @@
      * @param mes : error message.
      */
     private void error(String mes) {
-        System.out.println("An error occurs during the pojoization : " + mes);
         m_errors.add(mes);
     }
 
@@ -98,7 +98,6 @@
      * @param mes : warning message
      */
     public void warn(String mes) {
-        System.out.println("An warning occurs during the pojoization : " + mes);
         m_warnings.add(mes);
     }
 
@@ -356,6 +355,12 @@
                 name += ".class";
                 componentClazzes.add(new ComponentInfo(name, meta[i]));
             }
+            if (meta[i].getName().equalsIgnoreCase("handler") && meta[i].containsAttribute("className")) {
+                String name = meta[i].getAttribute("classname");
+                name = name.replace('.', '/');
+                name += ".class";
+                componentClazzes.add(new ComponentInfo(name, meta[i]));
+            }
         }
         return componentClazzes;
     }
@@ -409,7 +414,7 @@
     private void setImports(Attributes att) {
         Map imports = parseHeader(att.getValue("Import-Package"));
         Map ver = new TreeMap();
-        ver.put("version", "0.7.3");
+        ver.put("version", "0.7.5");
         if (!imports.containsKey("org.apache.felix.ipojo")) {
             imports.put("org.apache.felix.ipojo", ver);
         }
@@ -426,21 +431,6 @@
             verCM.put("version", "1.3");
             imports.put("org.osgi.service.log", verCM);
         }
-        
-
-        // Add handler namespace
-        String[][] namespaces = computeHandlerNamespace();
-        for (int j = 0; j < namespaces.length; j++) {
-            for (int k = 0; k < namespaces[j].length; k++) {
-                if (!namespaces[j][k].equals("")) {
-                    int lastIndex = namespaces[j][k].lastIndexOf('.');
-                    String ns = namespaces[j][k].substring(0, lastIndex);
-                    if (!imports.containsKey(ns)) {
-                        imports.put(ns, new TreeMap());
-                    }
-                }
-            }
-        }
 
         // Add referred imports from the metadata
         for (int i = 0; i < m_referredPackages.size(); i++) {
@@ -467,18 +457,6 @@
     }
 
     /**
-     * Build the list of namespaces used in the metadata. (first-order only). 
-     * @return the list of namespaces [array of component [ array of namespace ] ].
-     */
-    private String[][] computeHandlerNamespace() {
-        String[][] ns = new String[m_metadata.length][];
-        for (int i = 0; i < m_metadata.length; i++) {
-            ns[i] = m_metadata[i].getNamespaces();
-        }
-        return ns;
-    }
-
-    /**
      * Standard OSGi header parser. This parser can handle the format clauses ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '=' value )
      * This is mapped to a Map { name => Map { attr|directive => value } }
      * 
@@ -588,7 +566,7 @@
             stream.close();
 
         } catch (MalformedURLException e) {
-            error("Malformed Mtadata URL for " + path);
+            error("Malformed Metadata URL for " + path);
             return null;
         } catch (IOException e) {
             error("Cannot open the file : " + path);
@@ -619,19 +597,19 @@
     
     /**
      * Get packages referenced by composite.
-     * 
      * @return the list of referenced packages.
      */
     private List getReferredPackages() {
         List referred = new ArrayList();
         for (int i = 0; i < m_metadata.length; i++) {
             if (m_metadata[i].getName().equalsIgnoreCase("composite")) {
-                for (int j = 0; j < m_metadata[i].getElements().length; j++) {
-                    if (m_metadata[i].getElements()[j].containsAttribute("specification")) {
-                        String p = m_metadata[i].getElements()[j].getAttribute("specification");
-                        int last = p.lastIndexOf('.');
+                Element[] elems = m_metadata[i].getElements();
+                for (int j = 0; j < elems.length; j++) {
+                    String att = elems[j].getAttribute("specification");
+                    if (att != null) {
+                        int last = att.lastIndexOf('.');
                         if (last != -1) {
-                            referred.add(p.substring(0, last));
+                            referred.add(att.substring(0, last));
                         }
                     }
                 }
@@ -654,16 +632,19 @@
             result = actual + element.getNameSpace() + ":" + element.getName() + " { ";
         }
 
-        for (int i = 0; i < element.getAttributes().length; i++) {
-            if (element.getAttributes()[i].getNameSpace().equals("")) {
-                result = result + "$" + element.getAttributes()[i].getName() + "=\"" + element.getAttributes()[i].getValue() + "\" ";
+        Attribute[] atts = element.getAttributes();
+        for (int i = 0; i < atts.length; i++) {
+            Attribute current = (Attribute) atts[i];
+            if (current.getNameSpace().equals("")) {
+                result = result + "$" + current.getName() + "=\"" + current.getValue() + "\" ";
             } else {
-                result = result + "$" + element.getAttributes()[i].getNameSpace() + ":" + element.getAttributes()[i].getName() + "=\"" + element.getAttributes()[i].getValue() + "\" ";
+                result = result + "$" + current.getNameSpace() + ":" + current.getName() + "=\"" + current.getValue() + "\" ";
             }
         }
 
-        for (int i = 0; i < element.getElements().length; i++) {
-            result = buildManifestMetadata(element.getElements()[i], result);
+        Element[] elems = element.getElements();
+        for (int i = 0; i < elems.length; i++) {
+            result = buildManifestMetadata(elems[i], result);
         }
 
         return result + "}";

Modified: felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java?rev=579239&r1=579238&r2=579239&view=diff
==============================================================================
--- felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java (original)
+++ felix/trunk/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/xml/parser/XMLMetadataParser.java Tue Sep 25 06:27:49 2007
@@ -41,13 +41,13 @@
      * Get component type metadata. (both component and composite)
      * 
      * @return a components metadata
-     * @throws ParseException
-     *             when an error occurs in the xml parsing
+     * @throws ParseException :  occurs in the xml parsing
      */
     public Element[] getComponentsMetadata() throws ParseException {
         Element[] comp = m_elements[0].getElements("Component");
         Element[] compo = m_elements[0].getElements("Composite");
-        Element[] metadata = new Element[comp.length + compo.length];
+        Element[] handl = m_elements[0].getElements("Handler");
+        Element[] metadata = new Element[comp.length + compo.length + handl.length];
         int l = 0;
         for (int i = 0; i < comp.length; i++) {
             metadata[l] = comp[i];
@@ -57,6 +57,10 @@
             metadata[l] = compo[i];
             l++;
         }
+        for (int i = 0; i < handl.length; i++) {
+            metadata[l] = handl[i];
+            l++;
+        }
         return metadata;
     }
 
@@ -70,7 +74,8 @@
         Element[] comp = m_elements[0].getElements("Component");
         Element[] compo = m_elements[0].getElements("Composite");
         Element[] conf = m_elements[0].getElements("Instance");
-        Element[] metadata = new Element[comp.length + conf.length + compo.length];
+        Element[] handl = m_elements[0].getElements("Handler");
+        Element[] metadata = new Element[comp.length + conf.length + compo.length + handl.length];
         int l = 0;
         for (int i = 0; i < comp.length; i++) {
             metadata[l] = comp[i];
@@ -84,6 +89,10 @@
             metadata[l] = conf[i];
             l++;
         }
+        for (int i = 0; i < handl.length; i++) {
+            metadata[l] = handl[i];
+            l++;
+        }
         return metadata;
     }
 
@@ -123,12 +132,12 @@
         // Get the last element of the list
         Element lastElement = removeLastElement();
 
-        // Check if the name is consitent with the name of this end tag
-        if (!lastElement.getName().equalsIgnoreCase(qName) && !lastElement.getNameSpace().equals(namespaceURI)) {
+        // Check if the name is consistent with the name of this end tag
+        if (!lastElement.getName().equalsIgnoreCase(qName) && !lastElement.getNameSpace().equalsIgnoreCase(namespaceURI)) {
             throw new SAXException("Parse error when ending an element : " + qName + " [" + namespaceURI + "]");
         }
 
-        // The name is consitent
+        // The name is consistent
         // Add this element last element with if it is not the root
         if (m_elements.length != 0) {
             Element newQueue = m_elements[m_elements.length - 1];

Modified: felix/trunk/ipojo/metadata/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/metadata/pom.xml?rev=579239&r1=579238&r2=579239&view=diff
==============================================================================
--- felix/trunk/ipojo/metadata/pom.xml (original)
+++ felix/trunk/ipojo/metadata/pom.xml Tue Sep 25 06:27:49 2007
@@ -8,7 +8,7 @@
   <modelVersion>4.0.0</modelVersion>
   <artifactId>org.apache.felix.ipojo.metadata</artifactId>
   <packaging>bundle</packaging>
-  <version>0.7.3-SNAPSHOT</version>
+  <version>0.7.5-SNAPSHOT</version>
   <name>Apache Felix iPOJO Metadata</name>
   <build>
   <plugins>

Modified: felix/trunk/ipojo/metadata/src/main/java/org/apache/felix/ipojo/metadata/Element.java
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/metadata/src/main/java/org/apache/felix/ipojo/metadata/Element.java?rev=579239&r1=579238&r2=579239&view=diff
==============================================================================
--- felix/trunk/ipojo/metadata/src/main/java/org/apache/felix/ipojo/metadata/Element.java (original)
+++ felix/trunk/ipojo/metadata/src/main/java/org/apache/felix/ipojo/metadata/Element.java Tue Sep 25 06:27:49 2007
@@ -18,6 +18,14 @@
  */
 package org.apache.felix.ipojo.metadata;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * Element.
  * 
@@ -36,14 +44,18 @@
     private String m_nameSpace;
 
     /**
-     * List of the attributes of the element.
+     * Map of attributes of the element.
+     * The map key is the qualified name of the attribute (ns:name)
+     * The value is the attribute object.
      */
-    private Attribute[] m_attributes = new Attribute[0];
+    private Map m_attributes = new HashMap();
 
     /**
-     * List of the sub-element of the element.
+     * Map of the sub-element of the element.
+     * The map key is the element qualified name (ns:name).
+     * The value is the array of element of this name.
      */
-    private Element[] m_elements = new Element[0];
+    private Map m_elements = new HashMap();
 
     /**
      * Constructor.
@@ -52,7 +64,7 @@
      */
     public Element(String name, String ns) {
         m_name = name.toLowerCase();
-        m_nameSpace = ns;
+        m_nameSpace = ns.toLowerCase();
     }
 
     /**
@@ -60,7 +72,16 @@
      * @return the sub elements
      */
     public Element[] getElements() {
-        return m_elements;
+        Collection col = m_elements.values();
+        Iterator it = col.iterator();
+        List list = new ArrayList();
+        while (it.hasNext()) {
+            Element[] v = (Element[]) it.next();
+            for (int i = 0; i < v.length; i++) {
+                list.add(v[i]);
+            }
+        }
+        return (Element[]) list.toArray(new Element[0]);
     }
 
     /**
@@ -68,7 +89,7 @@
      * @return the attributes
      */
     public Attribute[] getAttributes() {
-        return m_attributes;
+        return (Attribute[]) m_attributes.values().toArray(new Attribute[0]);
     }
 
     /**
@@ -94,13 +115,12 @@
      */
     public String getAttribute(String name) {
         name = name.toLowerCase();
-        for (int i = 0; i < m_attributes.length; i++) {
-            if (m_attributes[i].getName().equals(name)) {
-                return m_attributes[i].getValue();
-            }
+        Attribute att = (Attribute) m_attributes.get(name);
+        if (att == null) {
+            return null;
+        } else {
+            return att.getValue();
         }
-        System.err.println("[Error in Metadata] The attribute " + name + " does not exist in " + m_name + " [" + m_nameSpace + "]");
-        return null;
     }
 
     /**
@@ -110,51 +130,35 @@
      * @return the String value of the attribute, or null if the attribute is not found.
      */
     public String getAttribute(String name, String ns) {
-        name = name.toLowerCase();
-        for (int i = 0; i < m_attributes.length; i++) {
-            if (m_attributes[i].getName().equals(name) && m_attributes[i].getNameSpace().equals(ns)) {
-                return m_attributes[i].getValue();
-            }
-        }
-        System.err.println("[Error in Metadata] The attribute " + name + "[" + ns + "] does not exist in " + m_name + " [" + m_nameSpace + "]");
-        return null;
+        name = ns.toLowerCase() + ":" + name.toLowerCase();
+        return getAttribute(name);
     }
-
+    
     /**
-     * Add a sub-element.
-     * @param elem : the element to add
+     * Get the qualified name of the current element.
+     * @return the qualified name of the current element.
      */
-    public void addElement(Element elem) {
-        for (int i = 0; (m_elements != null) && (i < m_elements.length); i++) {
-            if (m_elements[i] == elem) {
-                return;
-            }
-        }
-
-        if (m_elements != null) {
-            Element[] newElementsList = new Element[m_elements.length + 1];
-            System.arraycopy(m_elements, 0, newElementsList, 0, m_elements.length);
-            newElementsList[m_elements.length] = elem;
-            m_elements = newElementsList;
+    private String getQualifiedName() {
+        if (m_nameSpace.equals("")) {
+            return m_name;
         } else {
-            m_elements = new Element[] { elem };
+            return m_nameSpace + ":" + m_name;
         }
     }
 
     /**
-     * Add an element to the given list.
-     * @param list : the input list.
-     * @param elem : the element to add.
-     * @return the returned element list.
+     * Add a sub-element.
+     * @param elem : the element to add
      */
-    private static Element[] addElement(Element[] list, Element elem) {
-        if (list != null) {
-            Element[] newElementsList = new Element[list.length + 1];
-            System.arraycopy(list, 0, newElementsList, 0, list.length);
-            newElementsList[list.length] = elem;
-            return newElementsList;
+    public void addElement(Element elem) {
+        Element[] array = (Element[]) m_elements.get(elem.getQualifiedName());
+        if (array == null) {
+            m_elements.put(elem.getQualifiedName(), new Element[] {elem});
         } else {
-            return new Element[] { elem };
+            Element[] newElementsList = new Element[array.length + 1];
+            System.arraycopy(array, 0, newElementsList, 0, array.length);
+            newElementsList[array.length] = elem;
+            m_elements.put(elem.getQualifiedName(), newElementsList);
         }
     }
 
@@ -163,24 +167,29 @@
      * @param elem : the element to remove
      */
     public void removeElement(Element elem) {
-        int idx = -1;
-        for (int i = 0; i < m_elements.length; i++) {
-            if (m_elements[i] == elem) {
-                idx = i;
-                break;
+        Element[] array = (Element[]) m_elements.get(elem.getQualifiedName());
+        if (array == null) {
+            return;
+        } else {
+            int idx = -1;
+            for (int i = 0; i < array.length; i++) {
+                if (array[i] == elem) {
+                    idx = i;
+                    break;
+                }
             }
-        }
 
-        if (idx >= 0) {
-            if ((m_elements.length - 1) == 0) {
-                m_elements = new Element[0];
-            } else {
-                Element[] newElementsList = new Element[m_elements.length - 1];
-                System.arraycopy(m_elements, 0, newElementsList, 0, idx);
-                if (idx < newElementsList.length) {
-                    System.arraycopy(m_elements, idx + 1, newElementsList, idx, newElementsList.length - idx);
+            if (idx >= 0) {
+                if ((array.length - 1) == 0) {
+                    m_elements.remove(elem.getQualifiedName());
+                } else {
+                    Element[] newElementsList = new Element[array.length - 1];
+                    System.arraycopy(array, 0, newElementsList, 0, idx);
+                    if (idx < newElementsList.length) {
+                        System.arraycopy(array, idx + 1, newElementsList, idx, newElementsList.length - idx);
+                    }
+                    m_elements.put(elem.getQualifiedName(), newElementsList); // Update the stored list.
                 }
-                m_elements = newElementsList;
             }
         }
     }
@@ -190,20 +199,11 @@
      * @param att : the attribute to add
      */
     public void addAttribute(Attribute att) {
-        for (int i = 0; (m_attributes != null) && (i < m_attributes.length); i++) {
-            if (m_attributes[i] == att) {
-                return;
-            }
-        }
-
-        if (m_attributes != null) {
-            Attribute[] newAttributesList = new Attribute[m_attributes.length + 1];
-            System.arraycopy(m_attributes, 0, newAttributesList, 0, m_attributes.length);
-            newAttributesList[m_attributes.length] = att;
-            m_attributes = newAttributesList;
-        } else {
-            m_attributes = new Attribute[] { att };
+        String name = att.getName().toLowerCase();
+        if (!att.getNameSpace().equals("")) {
+            name = att.getNameSpace().toLowerCase() + ":" + name;
         }
+        m_attributes.put(name, att);
     }
 
     /**
@@ -211,26 +211,11 @@
      * @param att : the attribute to remove
      */
     public void removeAttribute(Attribute att) {
-        int idx = -1;
-        for (int i = 0; i < m_attributes.length; i++) {
-            if (m_attributes[i] == att) {
-                idx = i;
-                break;
-            }
-        }
-
-        if (idx >= 0) {
-            if ((m_attributes.length - 1) == 0) {
-                m_attributes = new Attribute[0];
-            } else {
-                Attribute[] newAttributesList = new Attribute[m_attributes.length - 1];
-                System.arraycopy(m_attributes, 0, newAttributesList, 0, idx);
-                if (idx < newAttributesList.length) {
-                    System.arraycopy(m_attributes, idx + 1, newAttributesList, idx, newAttributesList.length - idx);
-                }
-                m_attributes = newAttributesList;
-            }
+        String name = att.getName();
+        if (!att.getNameSpace().equals("")) {
+            name = att.getNameSpace() + ":" + name;
         }
+        m_attributes.remove(name);
     }
 
     /**
@@ -240,13 +225,12 @@
      */
     public Element[] getElements(String name) {
         name = name.toLowerCase();
-        Element[] list = new Element[0];
-        for (int i = 0; i < m_elements.length; i++) {
-            if (m_elements[i].getName().equalsIgnoreCase(name) && m_elements[i].getNameSpace().equals("")) {
-                list = Element.addElement(list, m_elements[i]);
-            }
+        Element[] elems = (Element[]) m_elements.get(name);
+        if (elems == null) {
+            return new Element[0];
+        } else {
+            return elems;
         }
-        return list;
     }
 
     /**
@@ -256,46 +240,34 @@
      * @return the resulting element array (empty if the search failed)
      */
     public Element[] getElements(String name, String ns) {
-        name = name.toLowerCase();
-        Element[] list = new Element[0];
-        for (int i = 0; i < m_elements.length; i++) {
-            if (m_elements[i].getName().equals(name) && m_elements[i].getNameSpace().equals(ns)) {
-                list = Element.addElement(list, m_elements[i]);
-            }
+        if (ns == null || ns.equals("")) {
+            return getElements(name);
         }
-        return list;
+        name = ns + ":" + name;
+        return getElements(name);
     }
 
     /**
-     * Is the element contains a sub-element of the type given in parameter. This method does not manage the namespace
+     * Is the element contains a sub-element of the type given in parameter.
      * @param name : type of the element to check.
      * @return true if the element contains an element of the type "name"
      */
     public boolean containsElement(String name) {
         name = name.toLowerCase();
-        for (int i = 0; i < m_elements.length; i++) {
-            if (m_elements[i].getName().equals(name)) {
-                return true;
-            }
-        }
-        return false;
+        return m_elements.containsKey(name);
     }
 
     /**
-     * Is the element contains a sub-element of the type given in parameter. This method does not manage the namespace
+     * Is the element contains a sub-element of the type given in parameter. 
      * @param name : type of the element to check.
      * @param ns : the namespace of the element to check.
      * @return true if the element contains an element of the type "name"
      */
     public boolean containsElement(String name, String ns) {
-        name = name.toLowerCase();
-        ns = ns.toLowerCase();
-        for (int i = 0; i < m_elements.length; i++) {
-            if (m_elements[i].getName().equals(name) && m_elements[i].getNameSpace().equals(ns)) {
-                return true;
-            }
+        if (ns != null && !ns.equals("")) {
+            name = ns + ":" + name;
         }
-        return false;
+        return containsElement(name);
     }
 
     /**
@@ -304,57 +276,9 @@
      * @return true if the element contains an attribute of the type "name"
      */
     public boolean containsAttribute(String name) {
-        name = name.toLowerCase();
-        for (int i = 0; i < m_attributes.length; i++) {
-            if (m_attributes[i].getName().equals(name)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Get used namespace.
-     * @return the first-order namespaces list of the current element. First-order namespace are namespace of the element attribute and namespaces of its direct sub-element.
-     */
-    public String[] getNamespaces() {
-        String[] ns = new String[0];
-
-        // Look for each direct sub-element
-        for (int i = 0; i < m_elements.length; i++) {
-            boolean found = false;
-            for (int j = 0; !found && j < ns.length; j++) {
-                if (ns[j].equals(m_elements[i].getNameSpace())) {
-                    found = true;
-                }
-            }
-            if (!found) {
-                String[] newNSList = new String[ns.length + 1];
-                System.arraycopy(ns, 0, newNSList, 0, ns.length);
-                newNSList[ns.length] = m_elements[i].getNameSpace();
-                ns = newNSList;
-            }
-        }
-
-        // Look for each attribute
-        for (int i = 0; i < m_attributes.length; i++) {
-            boolean found = false;
-            for (int j = 0; !found && j < ns.length; j++) {
-                if (ns[j].equals(m_attributes[i].getNameSpace())) {
-                    found = true;
-                }
-            }
-            if (!found) {
-                String[] newNSList = new String[ns.length + 1];
-                System.arraycopy(ns, 0, newNSList, 0, ns.length);
-                newNSList[ns.length] = m_attributes[i].getNameSpace();
-                ns = newNSList;
-            }
-        }
-
-        return ns;
+        return m_attributes.containsKey(name.toLowerCase());
     }
-
+    
     /**
      * Get the XML form of this element.
      * @return the XML snippet representing this element.
@@ -376,29 +300,36 @@
             tabs += "\t";
         }
 
-        if (m_nameSpace.equals("")) {
+        if ("".equals(m_nameSpace)) {
             xml = tabs + "<" + m_name;
         } else {
             xml = tabs + "<" + m_nameSpace + ":" + m_name;
         }
 
-        for (int i = 0; i < m_attributes.length; i++) {
-            Attribute current = m_attributes[i];
-            if (current.getNameSpace().equals("")) {
+        Set keys = m_attributes.keySet();
+        Iterator it = keys.iterator();
+        while (it.hasNext()) {
+            Attribute current = (Attribute) m_attributes.get(it.next());
+            if ("".equals(current.getNameSpace())) {
                 xml += " " + current.getName() + "=\"" + current.getValue() + "\"";
             } else {
                 xml += " " + current.getNameSpace() + ":" + current.getName() + "=\"" + current.getValue() + "\"";
             }
         }
 
-        if (m_elements.length == 0) {
+        if (m_elements.size() == 0) {
             xml += "/>";
             return xml;
         } else {
             xml += ">";
-            for (int i = 0; i < m_elements.length; i++) {
-                xml += "\n";
-                xml += m_elements[i].toXMLString(indent + 1);
+            keys = m_elements.keySet();
+            it = keys.iterator();
+            while (it.hasNext()) {
+                Element[] e = (Element[]) m_elements.get(it.next());
+                for (int i = 0; i < e.length; i++) {
+                    xml += "\n";
+                    xml += e[i].toXMLString(indent + 1);
+                }
             }
             xml += "\n" + tabs + "</" + m_name + ">";
             return xml;
@@ -427,27 +358,34 @@
             tabs += "\t";
         }
 
-        if (m_nameSpace.equals("")) {
+        if ("".equals(m_nameSpace)) {
             xml = tabs + m_name;
         } else {
             xml = tabs + m_nameSpace + ":" + m_name;
         }
 
-        for (int i = 0; i < m_attributes.length; i++) {
-            Attribute current = m_attributes[i];
-            if (current.getNameSpace().equals("")) {
+        Set keys = m_attributes.keySet();
+        Iterator it = keys.iterator();
+        while (it.hasNext()) {
+            Attribute current = (Attribute) m_attributes.get(it.next());
+            if ("".equals(current.getNameSpace())) {
                 xml += " " + current.getName() + "=\"" + current.getValue() + "\"";
             } else {
                 xml += " " + current.getNameSpace() + ":" + current.getName() + "=\"" + current.getValue() + "\"";
             }
         }
 
-        if (m_elements.length == 0) {
+        if (m_elements.size() == 0) {
             return xml;
         } else {
-            for (int i = 0; i < m_elements.length; i++) {
-                xml += "\n";
-                xml += m_elements[i].toString(indent + 1);
+            keys = m_elements.keySet();
+            it = keys.iterator();
+            while (it.hasNext()) {
+                Element[] e = (Element[]) m_elements.get(it.next());
+                for (int i = 0; i < e.length; i++) {
+                    xml += "\n";
+                    xml += e[i].toString(indent + 1);
+                }
             }
             return xml;
         }

Modified: felix/trunk/ipojo/plugin/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/ipojo/plugin/pom.xml?rev=579239&r1=579238&r2=579239&view=diff
==============================================================================
--- felix/trunk/ipojo/plugin/pom.xml (original)
+++ felix/trunk/ipojo/plugin/pom.xml Tue Sep 25 06:27:49 2007
@@ -7,7 +7,7 @@
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>org.apache.felix.ipojo.plugin</artifactId>
-  <version>0.7.3-SNAPSHOT</version>
+  <version>0.7.5-SNAPSHOT</version>
   <name>Apache Felix iPOJO Maven Plugin</name>
   <packaging>maven-plugin</packaging>
   <dependencies>
@@ -44,7 +44,7 @@
     <dependency>
       <groupId>${pom.groupId}</groupId>
       <artifactId>org.apache.felix.ipojo.manipulator</artifactId>
-      <version>0.7.3-SNAPSHOT</version>
+      <version>0.7.5-SNAPSHOT</version>
     </dependency>
   </dependencies>
 </project>