blob: ee39ead0c2068af9c9d63007232e0bac1d474ff6 [file] [log] [blame]
/*
* Copyright 2006 The Apache Software Foundation
*
* Licensed 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.handlers.dependency;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import org.apache.felix.ipojo.ComponentManager;
import org.apache.felix.ipojo.Handler;
import org.apache.felix.ipojo.Activator;
import org.apache.felix.ipojo.handlers.dependency.nullable.NullableObjectWriter;
import org.apache.felix.ipojo.metadata.Element;
/**
* The dependency handler manages a list of dependencies.
* @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
*/
public class DependencyHandler implements Handler {
/**
* The component manager using this handler.
*/
private ComponentManager m_componentManager;
/**
* List of depednencies of the component.
*/
private Dependency[] m_dependencies = new Dependency[0];
/**
* List of nullable class for optional dependencies.
*/
private Class[] m_nullableClasses = new Class[0];
/**
* Classloader to use to load nullable classes.
*/
private NullableClassloader m_classloader;
/**
* State of the handler.
*/
private int m_state;
// ===================== Fields getters & setters =====================
private void addDependency(Dependency dep) {
for (int i = 0; (m_dependencies != null) && (i < m_dependencies.length); i++) {
if (m_dependencies[i] == dep) {
return;
}
}
if (m_dependencies.length > 0) {
Dependency[] newDep = new Dependency[m_dependencies.length + 1];
System.arraycopy(m_dependencies, 0, newDep, 0, m_dependencies.length);
newDep[m_dependencies.length] = dep;
m_dependencies = newDep;
}
else {
m_dependencies = new Dependency[] {dep};
}
}
private void addNullableClass(Class clazz) {
for (int i = 0; (m_nullableClasses != null) && (i < m_nullableClasses.length); i++) {
if (m_nullableClasses[i] == clazz) {
return;
}
}
if (m_nullableClasses.length > 0) {
Class[] newClass = new Class[m_nullableClasses.length + 1];
System.arraycopy(m_nullableClasses, 0, newClass, 0, m_nullableClasses.length);
newClass[m_nullableClasses.length] = clazz;
m_nullableClasses = newClass;
}
else {
m_nullableClasses = new Class[] {clazz};
}
}
/**
* @return the dependency list
*/
public Dependency[] getDependencies() { return m_dependencies; }
/**
* @return the component manager
*/
protected ComponentManager getComponentManager() { return m_componentManager; }
// ===================== Handler implementation =====================
/**
* Check the validity of the dependencies.
*/
protected void checkContext() {
synchronized (this) {
Activator.getLogger().log(Level.INFO, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] Check Context ...");
// Store the initial state
int initialState = m_state;
// Check the component dependencies
if (!validateComponentDependencies()) {
// The dependencies are not valid
if (initialState == ComponentManager.VALID) {
//There is a state change
m_state = ComponentManager.INVALID;
m_componentManager.check();
}
// Else do nothing, the component state stay UNRESOLVED
}
else {
// The dependencies are valid
if (initialState == ComponentManager.INVALID) {
//There is a state change
m_state = ComponentManager.VALID;
m_componentManager.check();
}
// Else do nothing, the component state stay VALID
}
}
}
private boolean checkDependency(Dependency dep) {
// Check the internal type of dependency
String field = dep.getMetadata().getField();
Element manipulation = m_componentManager.getComponentMetatada().getMetadata().getElements("Manipulation")[0];
String type = null;
for (int i = 0; i < manipulation.getElements("Field").length; i++) {
if (field.equals(manipulation.getElements("Field")[i].getAttribute("name"))) {
type = manipulation.getElements("Field")[i].getAttribute("type");
break;
}
}
if (type == null) {
Activator.getLogger().log(Level.SEVERE, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] A declared dependency was not found in the class : " + dep.getMetadata().getField());
return false;
}
if (type != null) {
if (type.endsWith("[]")) {
// Set the dependency to multiple
dep.getMetadata().setMultiple();
type = type.substring(0, type.length() - 2);
}
if (dep.getMetadata().getServiceSpecification() == null) { dep.getMetadata().setServiceSpecification(type); }
if (!dep.getMetadata().getServiceSpecification().equals(type)) {
Activator.getLogger().log(Level.WARNING, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] The field type [" + type + "] and the needed service interface [" + dep.getMetadata().getServiceSpecification() + "] are not the same");
dep.getMetadata().setServiceSpecification(type);
}
}
else {
Activator.getLogger().log(Level.WARNING, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] The declared dependency " + dep.getMetadata().getField() + " does not exist in the code");
}
return true;
}
/**
* @see org.apache.felix.ipojo.Handler#configure(org.apache.felix.ipojo.ComponentManager, org.apache.felix.ipojo.metadata.Element)
*/
public void configure(ComponentManager cm, Element componentMetadata) {
// Fix the component manager
m_componentManager = cm;
m_dependencies = new Dependency[0];
m_nullableClasses = new Class[0];
// Create the dependency according to the component metadata
Element[] deps = componentMetadata.getElements("Dependency");
for (int i = 0; i < deps.length; i++) {
// Create the dependency metadata
String field = deps[i].getAttribute("field");
String serviceSpecification = null;
if (deps[i].containsAttribute("interface")) { serviceSpecification = deps[i].getAttribute("interface"); }
String filter = "";
if (deps[i].containsAttribute("filter")) { filter = deps[i].getAttribute("filter"); }
boolean optional = false;
if (deps[i].containsAttribute("optional") && deps[i].getAttribute("optional").equals("true")) { optional = true; }
DependencyMetadata dm = new DependencyMetadata(field, serviceSpecification, filter, optional);
Dependency dep = new Dependency(this, dm);
// Check the dependency :
if (checkDependency(dep)) { addDependency(dep); }
else { Activator.getLogger().log(Level.SEVERE, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] The dependency on " + dep.getMetadata().getField() + " is not valid"); }
// Look for dependency callback :
for (int j = 0; j < (deps[i].getElements("Callback", "")).length; j++) {
String method = deps[i].getElements("Callback", "")[j].getAttribute("method");
String type = deps[i].getElements("Callback", "")[j].getAttribute("type");
int methodType = 0;
if (type.equals("bind")) { methodType = DependencyCallback.BIND; }
else { methodType = DependencyCallback.UNBIND; }
boolean isStatic = false;
if (deps[i].getElements("Callback", "")[j].containsAttribute("isStatic") && deps[i].getElements("Callback", "")[j].getAttribute("isStatic").equals("true")) { isStatic = true; }
DependencyCallback dc = new DependencyCallback(dep, method, methodType, isStatic);
dep.getMetadata().addDependencyCallback(dc);
}
}
if (deps.length > 0) {
m_componentManager.register(this);
// Create the nullable classloader
m_classloader = new NullableClassloader(m_componentManager.getContext().getBundle());
}
}
private void createNullableClass(Dependency dep) {
Activator.getLogger().log(Level.INFO, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] Try to load the nullable class for " + dep.getMetadata().getServiceSpecification());
// Try to load the nullable object :
String[] segment = dep.getMetadata().getServiceSpecification().split("[.]");
String className = "org/apache/felix/ipojo/" + segment[segment.length - 1] + "Nullable";
String resource = dep.getMetadata().getServiceSpecification().replace('.', '/') + ".class";
URL url = m_componentManager.getContext().getBundle().getResource(resource);
try {
byte[] b = NullableObjectWriter.dump(url, dep.getMetadata().getServiceSpecification());
// // DEBUG :
// try {
// File file = new File("P:\\workspace\\iPOJO\\adapted\\" + className.replace('/', '.') + ".class");
// file.createNewFile();
// FileOutputStream fos = new FileOutputStream(file);
// fos.write(b);
// fos.close();
// } catch (Exception e3) {
// System.err.println("Problem to write the adapted class on the file system : " + e3.getMessage());
//
// }
addNullableClass(m_classloader.defineClass(className.replace('/', '.'), b, null));
Activator.getLogger().log(Level.INFO, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] Nullable class created for " + dep.getMetadata().getServiceSpecification());
} catch (IOException e1) {
Activator.getLogger().log(Level.SEVERE, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] Cannot open a stream of an interface to generate the nullable class for " + dep.getMetadata().getServiceSpecification() + " -> " + e1.getMessage());
} catch (Exception e2) {
Activator.getLogger().log(Level.SEVERE, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] Cannot load the nullable class for " + dep.getMetadata().getServiceSpecification() + " -> " + e2.getMessage());
}
}
/**
* Return the nullable class corresponding to the given name.
* @param name the needed type
* @return the class correspondig to the name, or null if the class does not exist.
*/
protected Class getNullableClass(String name) {
for (int i = 0; i < m_nullableClasses.length; i++) {
Class c = m_nullableClasses[i];
if (c.getName().equals(name)) {
return c;
}
}
return null;
}
/**
* @see org.apache.felix.ipojo.Handler#getterCallback(java.lang.String, java.lang.Object)
*/
public Object getterCallback(String fieldName, Object value) {
for (int i = 0; i < m_dependencies.length; i++) {
Dependency dep = m_dependencies[i];
if (dep.getMetadata().getField().equals(fieldName)) {
// The field name is a dependency, return the get
return dep.get();
}
}
// Else return the value
return value;
}
/**
* @see org.apache.felix.ipojo.Handler#isValid()
*/
public boolean isValid() {
return (m_state == ComponentManager.VALID);
}
/**
* @see org.apache.felix.ipojo.Handler#setterCallback(java.lang.String, java.lang.Object)
*/
public void setterCallback(String fieldName, Object value) {
// Do nothing
}
/**
* @see org.apache.felix.ipojo.Handler#start()
*/
public void start() {
Activator.getLogger().log(Level.INFO, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] Start the dependency handler");
// Start the dependencies, for optional dependencies create Nullable class
for (int i = 0; i < m_dependencies.length; i++) {
Dependency dep = m_dependencies[i];
if (dep.getMetadata().isOptional() && !dep.getMetadata().isMultiple()) { createNullableClass(dep); }
dep.start();
}
// Check the state
m_state = m_componentManager.getState();
checkContext();
}
/**
* @see org.apache.felix.ipojo.Handler#stateChanged(int)
*/
public void stateChanged(int state) {
// Another handler or the component manager itself change the state
if (m_state == ComponentManager.VALID && state == ComponentManager.INVALID) {
// The component is stopped => Stop the dependency tracking
stop();
}
m_state = state;
}
/**
* @see org.apache.felix.ipojo.Handler#stop()
*/
public void stop() {
for (int i = 0; i < m_dependencies.length; i++) { m_dependencies[i].stop(); }
}
private boolean validateComponentDependencies() {
boolean valide = true;
for (int i = 0; i < m_dependencies.length; i++) {
Dependency dep = m_dependencies[i];
valide = valide & dep.isSatisfied();
if (!valide) {
Activator.getLogger().log(Level.INFO, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] Component Dependencies are not valid : " + dep.getMetadata().getServiceSpecification());
return false;
}
}
Activator.getLogger().log(Level.INFO, "[DependencyHandler on " + m_componentManager.getComponentMetatada().getClassName() + "] Component Dependencies are valid");
return valide;
}
}