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;
020
021import org.apache.felix.ipojo.architecture.ComponentTypeDescription;
022import org.apache.felix.ipojo.metadata.Attribute;
023import org.apache.felix.ipojo.metadata.Element;
024import org.apache.felix.ipojo.parser.ParseUtils;
025import org.apache.felix.ipojo.parser.PojoMetadata;
026import org.apache.felix.ipojo.util.Log;
027import org.apache.felix.ipojo.util.Logger;
028import org.apache.felix.ipojo.util.Tracker;
029import org.apache.felix.ipojo.util.TrackerCustomizer;
030import org.osgi.framework.Bundle;
031import org.osgi.framework.BundleContext;
032import org.osgi.framework.InvalidSyntaxException;
033import org.osgi.framework.ServiceReference;
034
035import java.security.ProtectionDomain;
036import java.util.*;
037
038/**
039 * The component factory manages component instance objects. This management
040 * consists to create and manage component instances build with the current
041 * component factory. If the factory is public a {@link Factory} service is exposed.
042 *
043 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
044 * @see org.apache.felix.ipojo.Factory
045 * @see org.apache.felix.ipojo.IPojoFactory
046 * @see org.apache.felix.ipojo.util.TrackerCustomizer
047 */
048public class ComponentFactory extends IPojoFactory implements TrackerCustomizer {
049
050    /**
051     * System property set to automatically attach primitive handlers to primitive
052     * component types.
053     * The value is a String parsed as a list (comma separated). Each element is
054     * the fully qualified name of the handler <code>namespace:name</code>.
055     */
056    public static final String HANDLER_AUTO_PRIMITIVE = "org.apache.felix.ipojo.handler.auto.primitive";
057    /**
058     * The tracker used to track required handler factories.
059     * Immutable once set.
060     */
061    protected Tracker m_tracker;
062    /**
063     * The class loader to delegate classloading.
064     * Immutable once set.
065     */
066    private FactoryClassloader m_classLoader;
067    /**
068     * The component implementation class.
069     * (manipulated byte array)
070     */
071    private byte[] m_clazz;
072    /**
073     * The component implementation qualified class name.
074     * Immutable once set.
075     * This attribute is set during the creation of the factory.
076     */
077    private String m_classname;
078    /**
079     * The manipulation metadata of the implementation class.
080     * Immutable once set.
081     * This attribute is set during the creation of the factory.
082     */
083    private PojoMetadata m_manipulation;
084    /**
085     * A flag enabling / disabling the use of the Factory classloader to define the class.
086     * This flag must be enabled if the component class was manipulated on the fly.
087     */
088    private boolean m_useFactoryClassloader = false;
089
090    /**
091     * Creates a instance manager factory.
092     * The class is given in parameter. The component type is not a composite.
093     *
094     * @param context the bundle context
095     * @param clazz   the component class
096     * @param element the metadata of the component
097     * @throws ConfigurationException if the element describing the factory is malformed.
098     */
099    public ComponentFactory(BundleContext context, byte[] clazz, Element element) throws ConfigurationException {
100        this(context, element);
101        m_clazz = clazz;
102    }
103
104    /**
105     * Creates a instance manager factory.
106     *
107     * @param context the bundle context
108     * @param element the metadata of the component to create
109     * @throws ConfigurationException if element describing the factory is malformed.
110     */
111    public ComponentFactory(BundleContext context, Element element) throws ConfigurationException {
112        super(context, element);
113        check(element); // NOPMD. This invocation is normal.
114    }
115
116    /**
117     * Sets the flag enabling / disabling the factory classloader.
118     *
119     * @param use <code>true</code> enables the factory classloader.
120     */
121    public void setUseFactoryClassloader(boolean use) {
122        m_useFactoryClassloader = use;
123    }
124
125    /**
126     * Gets the component type description of the current factory.
127     *
128     * @return the description of the component type attached to this factory.
129     * @see org.apache.felix.ipojo.IPojoFactory#getComponentTypeDescription()
130     */
131    public ComponentTypeDescription getComponentTypeDescription() {
132        return new PrimitiveTypeDescription(this);
133    }
134
135    /**
136     * Allows a factory to check if the given element is well-formed.
137     * A component factory metadata is correct if they contain the 'classname' attribute.
138     * As this method is called from the (single-threaded) constructor, no synchronization is needed.
139     *
140     * @param element the metadata describing the component
141     * @throws ConfigurationException if the element describing the factory is malformed.
142     */
143    public void check(Element element) throws ConfigurationException {
144        m_classname = element.getAttribute("classname");
145        if (m_classname == null) {
146            throw new ConfigurationException("A component needs a class name : " + element);
147        }
148        m_manipulation = new PojoMetadata(m_componentMetadata);
149    }
150
151    /**
152     * Gets the class name.
153     * No synchronization needed, the classname is immutable.
154     *
155     * @return the class name.
156     * @see org.apache.felix.ipojo.IPojoFactory#getClassName()
157     */
158    public String getClassName() {
159        return m_classname;
160    }
161
162    /**
163     * Creates a primitive instance.
164     * This method is called when holding the lock.
165     *
166     * @param config   the instance configuration
167     * @param context  the service context (null if the instance has to be created in the global space).
168     * @param handlers the handlers to attach to the instance
169     * @return the created instance
170     * @throws org.apache.felix.ipojo.ConfigurationException
171     *          if the configuration process failed.
172     * @see org.apache.felix.ipojo.IPojoFactory#createInstance(java.util.Dictionary, org.apache.felix.ipojo.IPojoContext, org.apache.felix.ipojo.HandlerManager[])
173     */
174    public ComponentInstance createInstance(Dictionary config, IPojoContext context, HandlerManager[] handlers) throws org.apache.felix.ipojo.ConfigurationException {
175        InstanceManager instance = new InstanceManager(this, context, handlers);
176
177        try {
178            instance.configure(m_componentMetadata, config);
179            instance.start();
180            return instance;
181        } catch (ConfigurationException e) {
182            // An exception occurs while executing the configure or start
183            // methods, the instance is stopped so the architecture service is still published and so we can debug
184            // the issue.
185            instance.stop();
186            throw e;
187        } catch (Throwable e) { // All others exception are handled here.
188            // As for the previous case, the instance is stopped.
189            instance.stop();
190            m_logger.log(Logger.INFO, "An error occurred when creating an instance of " + getFactoryName(), e);
191            throw new ConfigurationException(e.getMessage(), e);
192        }
193
194    }
195
196    /**
197     * Defines a class.
198     * This method needs to be synchronized to avoid that the classloader
199     * is created twice.
200     * This method delegates the <code>define</code> method invocation to the
201     * factory classloader.
202     *
203     * @param name   the qualified name of the class
204     * @param clazz  the byte array of the class
205     * @param domain the protection domain of the class
206     * @return the defined class object
207     */
208    public synchronized Class<? extends Object> defineClass(String name, byte[] clazz, ProtectionDomain domain) {
209        if (!m_useFactoryClassloader) {
210            m_logger.log(Log.WARNING, "A class definition was required even without the factory classloader enabled");
211        }
212
213        if (m_classLoader == null) {
214            m_classLoader = new FactoryClassloader(this);
215        }
216        return m_classLoader.defineClass(name, clazz, domain);
217    }
218
219    /**
220     * Loads a class. This method checks if the class
221     * to load is the implementation class or not.
222     * If it is, the factory classloader is used, else
223     * the {@link Bundle#loadClass(String)} is called.
224     *
225     * The implementation class is loaded using the factory classloader only if the factory classloader was enabled
226     *
227     * @param className the name of the class to load
228     * @return the resulting Class object
229     * @throws ClassNotFoundException if the class is not found
230     * @see #setUseFactoryClassloader(boolean)
231     */
232    public Class loadClass(String className) throws ClassNotFoundException {
233        if (m_useFactoryClassloader && m_clazz != null && m_classname.equals(className)) {  // Immutable fields.
234            return defineClass(className, m_clazz, null);
235        }
236        return m_context.getBundle().loadClass(className);
237    }
238
239    /**
240     * Starts the factory.
241     * This method is not called when holding the monitor lock.
242     */
243    public void starting() {
244        if (m_tracker == null) {
245            if (m_requiredHandlers.size() != 0) {
246                try {
247                    String filter = "(&(" + Handler.HANDLER_TYPE_PROPERTY + "=" + PrimitiveHandler.HANDLER_TYPE + ")" + "(factory.state=1)" + ")";
248                    m_tracker = new Tracker(m_context, m_context.createFilter(filter), this);
249                    m_tracker.open();
250                } catch (InvalidSyntaxException e) {
251                    m_logger.log(Logger.ERROR, "A factory filter is not valid: " + e.getMessage()); //Holding the lock should not be an issue here.
252                    stop();
253                }
254            }
255        }
256        // Else, the tracking has already started.
257    }
258
259    /**
260     * Stops all the instance managers.
261     * This method is called when holding the lock.
262     */
263    public void stopping() {
264        if (m_tracker != null) {
265            m_tracker.close();
266            m_tracker = null;
267        }
268    }
269
270    /**
271     * Computes the factory name. The factory name is computed from
272     * the 'name' and 'classname' attributes.
273     * This method does not manipulate any non-immutable fields,
274     * so does not need to be synchronized.
275     *
276     * @return the factory name.
277     */
278    public String getFactoryName() {
279        String name = m_componentMetadata.getAttribute("name");
280        if (name == null) {
281            // No factory name, use the classname (mandatory attribute)
282            name = m_componentMetadata.getAttribute("classname");
283        }
284        return name;
285    }
286
287    /**
288     * Computes required handlers.
289     * This method does not manipulate any non-immutable fields,
290     * so does not need to be synchronized.
291     * This method checks the {@link ComponentFactory#HANDLER_AUTO_PRIMITIVE}
292     * system property to add the listed handlers to the required handler set.
293     *
294     * @return the required handler list.
295     */
296    public List<RequiredHandler> getRequiredHandlerList() {
297        List<RequiredHandler> list = new ArrayList<RequiredHandler>();
298        Element[] elems = m_componentMetadata.getElements();
299        for (Element current : elems) {
300            if (!"manipulation".equals(current.getName())) { // Remove the manipulation element
301                RequiredHandler req = new RequiredHandler(current.getName(), current.getNameSpace());
302                if (!list.contains(req)) {
303                    list.add(req);
304                }
305            }
306        }
307
308        // Add architecture if architecture != 'false'
309        String arch = m_componentMetadata.getAttribute("architecture");
310        if (arch == null || arch.equalsIgnoreCase("true")) {
311            list.add(new RequiredHandler("architecture", null));
312        }
313
314
315        // Determine if the component must be immediate.
316        // A component becomes immediate if it doesn't provide a service,
317        // and does not specified that the component is not immediate.
318        if (m_componentMetadata.getElements("provides") == null) {
319            String imm = m_componentMetadata.getAttribute("immediate");
320            if (imm == null) { // immediate not specified, set the immediate attribute to true
321                getLogger().log(
322                        Logger.INFO,
323                        "The component type " + getFactoryName()
324                                + " becomes immediate");
325                m_componentMetadata.addAttribute(new Attribute("immediate",
326                        "true"));
327            }
328        }
329
330        // Add lifecycle callback if immediate = true
331        RequiredHandler reqCallback = new RequiredHandler("callback", null);
332        String imm = m_componentMetadata.getAttribute("immediate");
333        if (!list.contains(reqCallback) && imm != null && imm.equalsIgnoreCase("true")) {
334            list.add(reqCallback);
335        }
336
337        // Manage auto attached handler.
338        String v = System.getProperty(HANDLER_AUTO_PRIMITIVE);
339        if (v != null && v.length() != 0) {
340            String[] hs = ParseUtils.split(v, ",");
341            for (String h1 : hs) {
342                String h = h1.trim();
343                String[] segments = ParseUtils.split(h, ":");
344                RequiredHandler rq = null;
345                if (segments.length == 2) { // External handler
346                    rq = new RequiredHandler(segments[1], segments[0]);
347                } else if (segments.length == 1) { // Core handler
348                    rq = new RequiredHandler(segments[1], null);
349                } // Others case are ignored.
350
351                if (rq != null) {
352                    // Check it's not already contained
353                    if (!list.contains(rq)) {
354                        list.add(rq);
355                    }
356                }
357            }
358        }
359
360        return list;
361    }
362
363    /**
364     * This method is called when a new handler factory is detected.
365     * Test if the factory can be used or not.
366     * This method need to be synchronized as it accesses to the content
367     * of required handlers.
368     *
369     * @param reference the new service reference.
370     * @return <code>true</code> if the given factory reference matches with a required handler.
371     * @see org.apache.felix.ipojo.util.TrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
372     */
373    public synchronized boolean addingService(ServiceReference reference) {
374        for (int i = 0; i < m_requiredHandlers.size(); i++) {
375            RequiredHandler req = (RequiredHandler) m_requiredHandlers.get(i);
376            if (req.getReference() == null && match(req, reference)) {
377                int oldP = req.getLevel();
378                req.setReference(reference);
379                // If the priority has changed, sort the list.
380                if (oldP != req.getLevel()) {
381                    // Manipulate the list.
382                    Collections.sort(m_requiredHandlers);
383                }
384                return true;
385            }
386        }
387        return false;
388    }
389
390    /**
391     * This method is called when a matching service has been added to the tracker,
392     * we can no compute the factory state. This method is synchronized to avoid
393     * concurrent calls to method modifying the factory state.
394     *
395     * @param reference the added service reference.
396     * @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
397     */
398    public synchronized void addedService(ServiceReference reference) {
399        if (m_state == INVALID) {
400            computeFactoryState();
401        }
402    }
403
404    /**
405     * This method is called when a used handler factory disappears.
406     * This method is synchronized to avoid concurrent calls to method modifying
407     * the factory state.
408     *
409     * @param reference the leaving service reference.
410     * @param service   the handler factory object.
411     * @see org.apache.felix.ipojo.util.TrackerCustomizer#removedService(org.osgi.framework.ServiceReference, java.lang.Object)
412     */
413    public synchronized void removedService(ServiceReference reference, Object service) {
414        // Look for the implied reference and invalid the handler identifier
415        for (Object m_requiredHandler : m_requiredHandlers) {
416            RequiredHandler req = (RequiredHandler) m_requiredHandler;
417            if (reference.equals(req.getReference())) {
418                req.unRef(); // This method will unget the service.
419                computeFactoryState();
420                return; // The factory can be used only once.
421            }
422        }
423    }
424
425    /**
426     * This method is called when a used handler factory is modified.
427     * However, handler factory modification is not possible, so this method
428     * is never called.
429     *
430     * @param reference the service reference
431     * @param service   the Factory object (if already get)
432     * @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
433     */
434    public void modifiedService(ServiceReference reference, Object service) {
435        // Noting to do
436    }
437
438    /**
439     * Returns manipulation metadata of this component type.
440     *
441     * @return manipulation metadata of this component type.
442     */
443    public PojoMetadata getPojoMetadata() {
444        return m_manipulation;
445    }
446
447    /**
448     * Gets the version of the component type.
449     *
450     * @return the version of <code>null</code> if not set.
451     * @see org.apache.felix.ipojo.Factory#getVersion()
452     */
453    public String getVersion() {
454        return m_version;
455    }
456
457    public ClassLoader getBundleClassLoader() {
458        return m_classLoader;
459    }
460
461}