/* * 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.api; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.apache.felix.ipojo.ComponentFactory; import org.apache.felix.ipojo.ConfigurationException; import org.apache.felix.ipojo.Factory; import org.apache.felix.ipojo.manipulation.Manipulator; import org.apache.felix.ipojo.metadata.Attribute; import org.apache.felix.ipojo.metadata.Element; import org.osgi.framework.BundleContext; /** * Allows defining primitive component types. * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class PrimitiveComponentType extends ComponentType { /** * The bundle context. */ private BundleContext m_context; /** * The implementation class name. */ private String m_classname; /** * The component type name. */ private String m_name; /** * The component type version. */ private String m_version; /** * Is the component type immediate. */ private boolean m_immediate; /** * Manipulation metadata of the component type. */ private Element m_manipulation; /** * Component factory attached to the component * type. */ private ComponentFactory m_factory; /** * List of provided services. */ private List<Service> m_services = new ArrayList<Service>(1); /** * List of service dependencies. */ private List<Dependency> m_dependencies = new ArrayList<Dependency>(); /** * List of configuration properties. */ private List<Property> m_properties = new ArrayList<Property>(); /** * The validate callback. */ private String m_validate; /** * The invalidate callback. */ private String m_invalidate; /** * The updated callback. */ private String m_updated; /** * Are the properties propagated to provided services? */ private boolean m_propagation; /** * The factory method. */ private String m_factoryMethod; /** * Is the factory public? */ private boolean m_public = true; /** * The Managed Service PID. */ private String m_msPID; /** * The temporal dependencies. */ private ArrayList<TemporalDependency> m_temporals = new ArrayList<TemporalDependency>(); /** * List of Handler representing external * handler configuration. */ private List<HandlerConfiguration> m_handlers = new ArrayList<HandlerConfiguration>(); /** * During the manipulation, we detect is the class is already manipulated. * If set to <code>false</code>, the factory is configured to use the factory classloader. */ private boolean m_alreadyManipulated = false; /** * Checks that the component type is not already * started. */ private void ensureNotInitialized() { if (m_factory != null) { throw new IllegalStateException("The component type was already initialized, cannot modify metadata"); } } /** * Checks that the component type description is valid. */ private void ensureValidity() { if (m_classname == null) { throw new IllegalStateException("The primitive component type has no implementation class"); } if (m_context == null) { throw new IllegalStateException("The primitive component type has no bundle context"); } } /** * Gets the component factory. * @return the factory attached to this component type. * @see org.apache.felix.ipojo.api.ComponentType#getFactory() */ public Factory getFactory() { initializeFactory(); return m_factory; } /** * Starts the component type. * @see org.apache.felix.ipojo.api.ComponentType#start() */ public void start() { initializeFactory(); m_factory.start(); } /** * Stops the component type. * @see org.apache.felix.ipojo.api.ComponentType#stop() */ public void stop() { initializeFactory(); m_factory.stop(); } /** * Initializes the factory. */ private void initializeFactory() { if (m_factory == null) { createFactory(); } } /** * Sets the bundle context. * @param bc the bundle context * @return the current component type */ public PrimitiveComponentType setBundleContext(BundleContext bc) { ensureNotInitialized(); m_context = bc; return this; } /** * Sets the implementation class. * @param classname the class name * @return the current component type */ public PrimitiveComponentType setClassName(String classname) { ensureNotInitialized(); m_classname = classname; return this; } /** * Sets the component type name. * @param name the factory name * @return the current component type */ public PrimitiveComponentType setComponentTypeName(String name) { ensureNotInitialized(); m_name = name; return this; } /** * Sets the component type version. * @param version the factory version or "bundle" to use the * bundle version. * @return the current component type */ public PrimitiveComponentType setComponentTypeVersion(String version) { ensureNotInitialized(); m_version = version; return this; } /** * Sets if the component type is immediate or not. * @param immediate <code>true</code> to set the component * type to immediate * @return the current component type */ public PrimitiveComponentType setImmediate(boolean immediate) { ensureNotInitialized(); m_immediate = immediate; return this; } /** * Sets the dependency factory method. * @param method the method used to create pojo object. * @return the current component type */ public PrimitiveComponentType setFactoryMethod(String method) { ensureNotInitialized(); m_factoryMethod = method; return this; } /** * Sets if the component type propagates properties to service properties. * @param propagation <code>true</code> to enable propagation * @return the current component type */ public PrimitiveComponentType setPropagation(boolean propagation) { ensureNotInitialized(); m_propagation = propagation; return this; } /** * Sets the factory public aspect. * @param visible <code>false</code> to create a private factory. * @return the current component type */ public PrimitiveComponentType setPublic(boolean visible) { ensureNotInitialized(); m_public = visible; return this; } /** * Sets the managed service pid. * @param pid the managed service pid * @return the current component type */ public PrimitiveComponentType setManagedServicePID(String pid) { ensureNotInitialized(); m_msPID = pid; return this; } /** * Sets the validate method. * @param method the validate method * @return the current component type */ public PrimitiveComponentType setValidateMethod(String method) { ensureNotInitialized(); m_validate = method; return this; } /** * Sets the invalidate method. * @param method the invalidate method * @return the current component type */ public PrimitiveComponentType setInvalidateMethod(String method) { ensureNotInitialized(); m_invalidate = method; return this; } /** * Sets the updated method. * @param method the updated method * @return the current component type */ public PrimitiveComponentType setUpdatedMethod(String method) { ensureNotInitialized(); m_updated = method; return this; } /** * Generates the component description. * @return the component type description of * the current component type */ private Element generateComponentMetadata() { Element element = new Element("component", ""); element.addAttribute(new Attribute("classname", m_classname)); if (m_name != null) { element.addAttribute(new Attribute("name", m_name)); } if (m_version != null) { element.addAttribute(new Attribute("version", m_version)); } if (m_factoryMethod != null) { element.addAttribute(new Attribute("factory-method", m_factoryMethod)); } if (! m_public) { element.addAttribute(new Attribute("public", "false")); } if (m_immediate) { element.addAttribute(new Attribute("immediate", "true")); } for (Service svc : m_services) { element.addElement(svc.getElement()); } for (Dependency dep : m_dependencies) { element.addElement(dep.getElement()); } for (TemporalDependency dep : m_temporals) { element.addElement(dep.getElement()); } if (m_validate != null) { Element callback = new Element("callback", ""); callback.addAttribute(new Attribute("transition", "validate")); callback.addAttribute(new Attribute("method", m_validate)); element.addElement(callback); } if (m_invalidate != null) { Element callback = new Element("callback", ""); callback.addAttribute(new Attribute("transition", "invalidate")); callback.addAttribute(new Attribute("method", m_invalidate)); element.addElement(callback); } // Properties // First determine if we need the properties element if (m_propagation || m_msPID != null || ! m_properties.isEmpty()) { Element properties = new Element("properties", ""); if (m_propagation) { properties.addAttribute(new Attribute("propagation", "true")); } if (m_msPID != null) { properties.addAttribute(new Attribute("pid", m_msPID)); } if (m_updated != null) { properties.addAttribute(new Attribute("updated", m_updated)); } for (Property prop : m_properties) { properties.addElement(prop.getElement()); } element.addElement(properties); } // External handlers for (HandlerConfiguration hc : m_handlers) { element.addElement(hc.getElement()); } return element; } /** * Adds an HandlerConfiguration to the component type. Each component type * implementation must uses the populated list (m_handlers) when generating * the component metadata. * @param handler the handler configuration to add * @return the current component type */ public PrimitiveComponentType addHandler(HandlerConfiguration handler) { m_handlers.add(handler); return this; } /** * Creates the component factory. */ private void createFactory() { ensureValidity(); byte[] clazz = manipulate(); Element meta = generateComponentMetadata(); meta.addElement(m_manipulation); try { if (m_alreadyManipulated) { // Already manipulated m_factory = new ComponentFactory(m_context, meta); } else { m_factory = new ComponentFactory(m_context, clazz, meta); m_factory.setUseFactoryClassloader(true); } m_factory.start(); } catch (ConfigurationException e) { throw new IllegalStateException("An exception occurs during factory initialization", e); } } /** * Manipulates the implementation class. * @return the manipulated class */ private byte[] manipulate() { Manipulator manipulator = new Manipulator(new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { return m_context.getBundle().loadClass(name); } catch (ClassNotFoundException e) { return this.getClass().getClassLoader().loadClass(name); } } }); try { byte[] array = getClassByteArray(); // Step 1 - preparation manipulator.prepare(array); byte[] newclazz = new byte[0]; if (!manipulator.isAlreadyManipulated()) { // Step 2 - manipulation newclazz = manipulator.manipulate(array); } m_manipulation = manipulator.getManipulationMetadata(); m_alreadyManipulated = manipulator.isAlreadyManipulated(); return newclazz; } catch (IOException e) { throw new IllegalStateException("An exception occurs during implementation class manipulation", e); } } /** * Gets a class file as a byte array. * @return the byte array. * @throws IOException the class file cannot be read. */ private byte[] getClassByteArray() throws IOException { String filename = m_classname.replace('.', '/') + ".class"; URL url = m_context.getBundle().getResource(filename); if (url == null) { throw new IllegalStateException("An exception occurs during implementation class manipulation : cannot found the class file " + filename); } InputStream is = url.openStream(); if (is == null) { throw new IllegalStateException("An exception occurs during implementation class manipulation : cannot read the class file " + url); } byte[] b = new byte[is.available()]; is.read(b); return b; } /** * Adds a provided service. * @param svc the service to add * @return the current component type */ public PrimitiveComponentType addService(Service svc) { ensureNotInitialized(); m_services.add(svc); return this; } /** * Adds a service dependency. * @param dep the dependency to add * @return the current component type */ public PrimitiveComponentType addDependency(Dependency dep) { ensureNotInitialized(); m_dependencies.add(dep); return this; } /** * Adds a temporal service dependency. * @param dep the temporal dependency to add * @return the current component type */ public PrimitiveComponentType addDependency(TemporalDependency dep) { ensureNotInitialized(); m_temporals.add(dep); return this; } /** * Adds a configuration property. * @param prop the property to add * @return the current component type */ public PrimitiveComponentType addProperty(Property prop) { ensureNotInitialized(); m_properties.add(prop); return this; } /** * Adds a configuration property. * @param key the key * @param obj the value (can be <code>null</code>) * @return the current component type */ public PrimitiveComponentType addProperty(String key, Object obj) { String value = null; if (obj != null) { value = obj.toString(); } addProperty(new Property().setName(key).setValue(value)); return this; } }