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 */
019package org.apache.felix.ipojo.composite;
020
021import java.util.ArrayList;
022import java.util.Dictionary;
023import java.util.List;
024
025import org.apache.felix.ipojo.ComponentFactory;
026import org.apache.felix.ipojo.ComponentInstance;
027import org.apache.felix.ipojo.ConfigurationException;
028import org.apache.felix.ipojo.Handler;
029import org.apache.felix.ipojo.HandlerFactory;
030import org.apache.felix.ipojo.HandlerManager;
031import org.apache.felix.ipojo.IPojoContext;
032import org.apache.felix.ipojo.MissingHandlerException;
033import org.apache.felix.ipojo.UnacceptableConfiguration;
034import org.apache.felix.ipojo.metadata.Element;
035import org.apache.felix.ipojo.util.Logger;
036import org.apache.felix.ipojo.util.Tracker;
037import org.apache.felix.ipojo.util.TrackerCustomizer;
038import org.osgi.framework.BundleContext;
039import org.osgi.framework.Constants;
040import org.osgi.framework.InvalidSyntaxException;
041
042/**
043 * The component factory manages component instance objects. This management
044 * consist in creating and managing component instance build with the component
045 * factory. This class could export Factory and ManagedServiceFactory services.
046 * 
047 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
048 */
049public class CompositeFactory extends ComponentFactory implements TrackerCustomizer {
050
051    /**
052     * Tracker used to track required handler factories.
053     */
054    protected Tracker m_tracker;
055
056    /**
057     * Create a composite factory.
058     * @param context : bundle context
059     * @param metadata : metadata of the component to create
060     * @throws ConfigurationException occurs when the element describing the factory is malformed.
061     */
062    public CompositeFactory(BundleContext context, Element metadata) throws ConfigurationException {
063        super(context, metadata);
064    }
065    
066    /**
067     * Check if the metadata are well formed.
068     * @param metadata : metadata
069     * @throws ConfigurationException occurs when the element describing the factory is malformed.
070     * @see org.apache.felix.ipojo.ComponentFactory#check(org.apache.felix.ipojo.metadata.Element)
071     */
072    public void check(Element metadata) throws ConfigurationException {
073        String name = metadata.getAttribute("name");
074        if (name == null) {
075            throw new ConfigurationException("A composite needs a name : " + metadata);
076        }
077    }
078    
079    public String getClassName() { return null; }
080
081    /**
082     * Compute required handlers.
083     * @return the list of required handler.
084     */
085    public List<RequiredHandler> getRequiredHandlerList() {
086        List<RequiredHandler> list = new ArrayList<RequiredHandler>();
087        Element[] elems = m_componentMetadata.getElements();
088        for (Element current : elems) {
089            RequiredHandler req = new RequiredHandler(current.getName(), current.getNameSpace());
090            if (!list.contains(req)) {
091                list.add(req);
092            }
093        }
094        
095        // Add architecture if architecture != 'false'
096        String arch = m_componentMetadata.getAttribute("architecture");
097        if (arch == null || arch.equalsIgnoreCase("true")) {
098            RequiredHandler req = new RequiredHandler("architecture", null);
099            if (! list.contains(req)) { list.add(req); }
100        }
101        
102        return list;
103    }
104    
105    /**
106     * Stop all the instance managers.
107     */
108    public synchronized void stopping() {
109        if (m_tracker != null) {
110            m_tracker.close();
111        }
112        m_tracker = null;
113    }
114
115    /**
116     * Start all the instance managers.
117     */
118    public synchronized void starting() {
119        if (m_requiredHandlers.size() != 0) {
120            try {
121                String filter = "(&(" + Constants.OBJECTCLASS + "=" + HandlerFactory.class.getName() + ")"
122                    + "(" + Handler.HANDLER_TYPE_PROPERTY + "=" + CompositeHandler.HANDLER_TYPE + ")" 
123                    + "(factory.state=1)"
124                    + ")";
125                m_tracker = new Tracker(m_context, m_context.createFilter(filter), this);
126                m_tracker.open();
127            } catch (InvalidSyntaxException e) {
128                m_logger.log(Logger.ERROR, "A factory filter is not valid: " + e.getMessage());
129                stop();
130            }
131        }
132    }
133    
134    /**
135     * Create an instance from the current factory.
136     * @param configuration : instance configuration
137     * @param context : bundle context to inject in the instance manager
138     * @param handlers : array of handler object to attached on the instance 
139     * @return the created instance
140     * @throws ConfigurationException either the instance configuration or the instance starting has failed 
141     * @see org.apache.felix.ipojo.ComponentFactory#createInstance(java.util.Dictionary, org.apache.felix.ipojo.IPojoContext, org.apache.felix.ipojo.HandlerManager[])
142     */
143    public ComponentInstance createInstance(Dictionary configuration, IPojoContext context, HandlerManager[] handlers) throws ConfigurationException {
144        CompositeManager inst = new CompositeManager(this, context, handlers);
145        inst.configure(m_componentMetadata, configuration);
146        inst.start();
147        return inst;
148    }
149
150    /**
151     * Reconfigure an existing instance.
152     * @param properties : the new configuration to push.
153     * @throws UnacceptableConfiguration : occurs if the new configuration is
154     * not consistent with the component type.
155     * @throws MissingHandlerException : occurs when an handler is unavailable when creating the instance.
156     * @see org.apache.felix.ipojo.Factory#reconfigure(java.util.Dictionary)
157     */
158    public synchronized void reconfigure(Dictionary properties) throws UnacceptableConfiguration, MissingHandlerException {
159        if (properties == null || (properties.get("instance.name") == null && properties.get("name") == null)) { // Support both instance.name and name
160            throw new UnacceptableConfiguration("The configuration does not contains the \"instance.name\" property");
161        }
162
163        String name = (String) properties.get("instance.name");
164        if (name == null) {
165            name = (String) properties.get("name");
166        }
167
168        ComponentInstance instance = m_componentInstances.get(name);
169        if (instance == null) { // The instance does not exists.
170            return;
171        }
172
173        instance.reconfigure(properties); // re-configure the component
174    }
175
176    public String getFactoryName() {
177        return m_componentMetadata.getAttribute("name"); // Mandatory attribute.
178    }
179
180}