blob: 53a09316302ebd2b7e475a227442e78338a3df10 [file] [log] [blame]
/*
* 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;
}
}