blob: 0b80e0e02340ee371c69b2aeed77f9a95d2807b0 [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;
import java.io.IOException;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.parser.ManifestMetadataParser;
import org.apache.felix.ipojo.parser.ParseException;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.service.log.LogService;
/**
* iPOJO Extender.
* Looks for iPOJO Bundle and start the management of these bundles if needed.
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class Extender implements SynchronousBundleListener, BundleActivator {
/**
* iPOJO Manifest header.
*/
private static final String IPOJO_HEADER = "iPOJO-Components";
/**
* iPOJO Bundle Context.
*/
private BundleContext m_context;
/**
* Dictionary of [BundleId, Factory List].
*/
private Dictionary m_components;
/**
* Dictionary of [BundleId, Instance Creator].
*/
private Dictionary m_creators;
/**
* iPOJO Bundle Id.
*/
private long m_bundleId;
/**
* Bundle Listener Notification.
* @param event : the bundle event.
* @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
*/
public void bundleChanged(BundleEvent event) {
if (event.getBundle().getBundleId() == m_bundleId) {
return;
}
switch (event.getType()) {
case BundleEvent.STARTED:
startManagementFor(event.getBundle());
break;
case BundleEvent.STOPPING:
closeManagementFor(event.getBundle());
break;
default:
break;
}
}
/**
* Ends the iPOJO Management for the given bundle.
* @param bundle : the bundle.
*/
private void closeManagementFor(Bundle bundle) {
ComponentFactory[] cfs = (ComponentFactory[]) m_components.get(bundle);
InstanceCreator creator = (InstanceCreator) m_creators.get(bundle);
if (cfs == null && creator == null) { return; }
for (int i = 0; cfs != null && i < cfs.length; i++) {
ComponentFactory factory = cfs[i];
factory.stop();
}
if (creator != null) { creator.stop(); }
m_components.remove(bundle);
m_creators.remove(bundle);
}
/**
* Check if the given bundle is an iPOJO bundle, and begin the iPOJO management is true.
* @param bundle : the bundle to check.
*/
private void startManagementFor(Bundle bundle) {
// Check bundle
Dictionary dict = bundle.getHeaders();
String header = (String) dict.get(IPOJO_HEADER);
if (header == null) {
return;
} else {
try {
parse(bundle, header);
} catch (IOException e) {
err("An exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
} catch (ParseException e) {
err("A parse exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
}
}
}
/**
* Parse the internal metadata (from the manifest (in the iPOJO-Components property)).
* @param bundle : the owner bundle.
* @param components : iPOJO Header String.
* @throws IOException : the manifest could not be found
* @throws ParseException : the parsing process failed
*/
private void parse(Bundle bundle, String components) throws IOException, ParseException {
ManifestMetadataParser parser = new ManifestMetadataParser();
parser.parseHeader(components);
Element[] componentsMetadata = parser.getComponentsMetadata(); // Get the component type declaration
for (int i = 0; i < componentsMetadata.length; i++) { addComponentFactory(bundle, componentsMetadata[i]); }
start(bundle, parser.getInstances());
}
/**
* iPOJO Starting method.
* @param bc : iPOJO bundle context.
* @throws Exception : the start method failed.
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext bc) throws Exception {
m_context = bc;
m_bundleId = bc.getBundle().getBundleId();
m_components = new Hashtable();
m_creators = new Hashtable();
synchronized (this) {
for (int i = 0; i < bc.getBundles().length; i++) {
if (bc.getBundles()[i].getState() == Bundle.ACTIVE) {
startManagementFor(bc.getBundles()[i]);
}
}
}
// listen to any changes in bundles
m_context.addBundleListener(this);
}
/**
* Stop the iPOJO Management.
* @param bc : bundle context.
* @throws Exception : the stop method failed.
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext bc) throws Exception {
m_context.removeBundleListener(this);
Enumeration e = m_components.keys();
while (e.hasMoreElements()) {
ComponentFactory[] cfs = (ComponentFactory[]) m_components.get(e.nextElement());
for (int i = 0; i < cfs.length; i++) {
cfs[i].stop();
}
}
m_components = null;
Enumeration e2 = m_creators.keys();
while (e2.hasMoreElements()) {
InstanceCreator creator = (InstanceCreator) m_creators.get(e2.nextElement());
creator.stop();
}
}
/**
* Add a component factory to the factory list.
* @param cm : the new component metadata.
* @param bundle : the bundle.
*/
private void addComponentFactory(Bundle bundle, Element cm) {
ComponentFactory factory = new ComponentFactory(bundle.getBundleContext(), cm);
ComponentFactory[] cfs = (ComponentFactory[]) m_components.get(bundle);
// If the factory array is not empty add the new factory at the end
if (cfs != null && cfs.length != 0) {
ComponentFactory[] newFactory = new ComponentFactory[cfs.length + 1];
System.arraycopy(cfs, 0, newFactory, 0, cfs.length);
newFactory[cfs.length] = factory;
cfs = newFactory;
m_components.put(bundle, cfs);
} else {
m_components.put(bundle, new ComponentFactory[] {factory}); // Else create an array of size one with the new Factory
}
}
/**
* Start the management factories and create instances.
* @param bundle : the bundle.
* @param confs : the instances to create.
*/
private void start(Bundle bundle, Dictionary[] confs) {
ComponentFactory[] cfs = (ComponentFactory[]) m_components.get(bundle);
// Start the factories
for (int j = 0; cfs != null && j < cfs.length; j++) {
cfs[j].start();
}
Dictionary[] outsiders = new Dictionary[0];
for (int i = 0; confs != null && i < confs.length; i++) {
Dictionary conf = confs[i];
boolean created = false;
for (int j = 0; cfs != null && j < cfs.length; j++) {
String componentClass = cfs[j].getComponentClassName();
String factoryName = cfs[j].getName();
String componentName = cfs[j].getComponentTypeName();
if (conf.get("component") != null && (conf.get("component").equals(componentClass) || conf.get("component").equals(factoryName)) || conf.get("component").equals(componentName)) {
try {
cfs[j].createComponentInstance(conf);
created = true;
} catch (UnacceptableConfiguration e) {
System.err.println("Cannot create the instance " + conf.get("name") + " : " + e.getMessage());
}
}
}
if (!created && conf.get("component") != null) {
if (outsiders.length != 0) {
Dictionary[] newList = new Dictionary[outsiders.length + 1];
System.arraycopy(outsiders, 0, newList, 0, outsiders.length);
newList[outsiders.length] = conf;
outsiders = newList;
} else {
outsiders = new Dictionary[] { conf };
}
}
}
// Create the instance creator if needed.
if (outsiders.length > 0) {
m_creators.put(bundle, new InstanceCreator(bundle.getBundleContext(), outsiders));
}
}
/**
* Log an error message in a log service (if available) and display the message in the console.
* @param message : the message to log
* @param t : an attached error (can be null)
*/
private void err(String message, Throwable t) {
ServiceReference ref = m_context.getServiceReference(LogService.class.getName());
if (ref != null) {
LogService log = (LogService) m_context.getService(ref);
log.log(LogService.LOG_ERROR, message, t);
m_context.ungetService(ref);
}
if (t != null) {
System.err.println("[iPOJO-Core] " + message + " : " + t.getMessage());
} else {
System.err.println("[iPOJO-Core] " + message);
}
}
}