/*
* 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.extender.internal.processor;
import org.apache.felix.ipojo.extender.internal.BundleProcessor;
import org.apache.felix.ipojo.extender.internal.declaration.DefaultInstanceDeclaration;
import org.apache.felix.ipojo.extender.internal.declaration.DefaultTypeDeclaration;
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.parser.ManifestMetadataParser;
import org.apache.felix.ipojo.parser.ParseException;
import org.apache.felix.ipojo.util.Log;
import org.apache.felix.ipojo.util.Logger;
import org.osgi.framework.Bundle;
import java.io.IOException;
import java.util.*;
/**
* Processor handling the {@link #IPOJO_HEADER} and {@link #IPOJO_HEADER_ALT}
* header from the bundle manifest.
*/
public class ComponentsBundleProcessor implements BundleProcessor {
/**
* iPOJO Component Type and Instance declaration header.
*/
public static final String IPOJO_HEADER = "iPOJO-Components";
/**
* iPOJO Component Type and Instance declaration header
* (alternative).
* This header was introduced because of BND supporting only header
* starting with an uppercase.
*/
public static final String IPOJO_HEADER_ALT = "IPOJO-Components";
/**
* The attribute used in instance configuration specifying the targeted component (i.e. factory).
*/
public static final String COMPONENT_INSTANCE_ATTRIBUTE = "component";
/**
* The logger.
*/
private final Log m_logger;
/**
* Registry storing the bundle to components and instances declared within this bundle.
*/
private final Map<Bundle, ComponentsAndInstances> m_registry = new HashMap<Bundle, ComponentsAndInstances>();
/**
* Creates the component bundle processor.
*
* @param logger the logger.
*/
public ComponentsBundleProcessor(Log logger) {
m_logger = logger;
}
/**
* A bundle is starting.
*
* @param bundle the bundle
*/
public void activate(Bundle bundle) {
Dictionary dict = bundle.getHeaders();
// Check bundle
String header = (String) dict.get(IPOJO_HEADER);
// Check the alternative header
if (header == null) {
header = (String) dict.get(IPOJO_HEADER_ALT);
}
if (header != null) {
try {
parse(bundle, header);
} catch (IOException e) {
m_logger.log(Logger.ERROR, "An exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
} catch (ParseException e) {
m_logger.log(Logger.ERROR, "A parse exception occurs during the parsing of the bundle " + bundle.getBundleId(), e);
}
}
}
/**
* A bundle is stopping.
*
* @param bundle the bundle
*/
public void deactivate(Bundle bundle) {
ComponentsAndInstances cai = m_registry.remove(bundle);
if (cai != null) {
cai.stop();
}
}
/**
* {@inheritDoc BundleProcessor#start}
*/
public void start() {
// Nothing to do
}
/**
* {@inheritDoc BundleProcessor#stop}
* <p/>
* This method cleans up all created factories and instances.
*/
public void stop() {
// Ignored, for a simple ordered shutdown, use ReverseBundleProcessor
}
/**
* Parses the internal metadata (from the manifest
* (in the iPOJO-Components property)). This methods
* creates factories and add instances to the instance creator.
*
* @param bundle the owner bundle.
* @param components The iPOJO Header String.
* @throws IOException if the manifest can not be found
* @throws ParseException if the parsing process failed
*/
private void parse(Bundle bundle, String components) throws IOException, ParseException {
ManifestMetadataParser parser = new ManifestMetadataParser();
parser.parseHeader(components);
// Get the component type declaration
Element[] metadata = parser.getComponentsMetadata();
for (int i = 0; i < metadata.length; i++) {
handleTypeDeclaration(bundle, metadata[i]);
}
Dictionary[] instances = parser.getInstances();
for (int i = 0; instances != null && i < instances.length; i++) {
handleInstanceDeclaration(bundle, instances[i]);
}
}
/**
* Extracts and builds the declaration attached to an instance.
*
* @param bundle the bundle declaring the instance
* @param instance the instance configuration (parsed from the header)
*/
private void handleInstanceDeclaration(Bundle bundle, Dictionary instance) {
String component = (String) instance.get(COMPONENT_INSTANCE_ATTRIBUTE);
//String v = (String) instance.get(Factory.FACTORY_VERSION_PROPERTY); //TODO CES to GSA, why this is commented ?
DefaultInstanceDeclaration declaration = new DefaultInstanceDeclaration(bundle.getBundleContext(),
component, instance);
declaration.start();
getComponentsAndInstances(bundle).m_instances.add(declaration);
}
/**
* Adds a component factory to the factory list.
*
* @param metadata the new component metadata.
* @param bundle the bundle.
*/
private void handleTypeDeclaration(Bundle bundle, Element metadata) {
DefaultTypeDeclaration declaration = new DefaultTypeDeclaration(bundle.getBundleContext(), metadata);
declaration.start();
getComponentsAndInstances(bundle).m_types.add(declaration);
}
/**
* Gets the {@link ComponentsAndInstances} declared by the given bundle.
*
* @param bundle the bundle
* @return the set of component and instances declared by the bundle, <code>null</code> otherwise
*/
private ComponentsAndInstances getComponentsAndInstances(Bundle bundle) {
ComponentsAndInstances cai = m_registry.get(bundle);
if (cai == null) {
cai = new ComponentsAndInstances();
m_registry.put(bundle, cai);
}
return cai;
}
/**
* Container storing the components and instances declared by a bundle.
* This class is not intended to be used outside from the current processor.
*/
private static class ComponentsAndInstances {
List<DefaultTypeDeclaration> m_types = new ArrayList<DefaultTypeDeclaration>();
List<DefaultInstanceDeclaration> m_instances = new ArrayList<DefaultInstanceDeclaration>();
/**
* Stops all declarations.
*/
void stop() {
for (DefaultInstanceDeclaration instance : m_instances) {
instance.stop();
}
for (DefaultTypeDeclaration declaration : m_types) {
declaration.stop();
}
m_instances.clear();
m_types.clear();
}
}
}