blob: aa0d866ff6e2ccc79d7dd6a9b516e3f38772a390 [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.composite.service.instantiator;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.felix.ipojo.ComponentInstance;
import org.apache.felix.ipojo.Factory;
import org.apache.felix.ipojo.ServiceContext;
import org.apache.felix.ipojo.UnacceptableConfiguration;
import org.apache.felix.ipojo.metadata.Element;
import org.apache.felix.ipojo.util.Logger;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
/**
* Manage a service instantiation. This service create component instance
* providing the required service specification.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class SvcInstance implements ServiceListener {
/**
* Required specification.
*/
private String m_specification;
/**
* Configuration to push to the instance.
*/
private Dictionary m_configuration;
/**
* Map of factory references => instance or NO_INSTANCE.
*/
private Map /* ServiceReference */m_usedRef = new HashMap();
/**
* Does we instantiate several provider ?
*/
private boolean m_isAggregate = false;
/**
* Is the service optional ?
*/
private boolean m_isOptional = false;
/**
* Handler creating the service instance.
*/
private ServiceInstantiatorHandler m_handler;
/**
* Service Context (internal scope).
*/
private ServiceContext m_context;
/**
* True if the service instantiation is valid.
*/
private boolean m_isValid = false;
/**
* String form of the factory filter.
*/
private String m_filterStr;
/**
* Constructor.
*
* @param h : the handler.
* @param spec : required specification.
* @param conf : instance configuration.
* @param isAgg : is the service instance an aggregate service ?
* @param isOpt : is the service instance optional ?
* @param filt : LDAP filter
*/
public SvcInstance(ServiceInstantiatorHandler h, String spec, Dictionary conf, boolean isAgg, boolean isOpt, String filt) {
m_handler = h;
m_context = h.getManager().getServiceContext();
m_specification = spec;
m_configuration = conf;
m_isAggregate = isAgg;
m_isOptional = isOpt;
m_filterStr = filt;
}
/**
* Start the service instance.
*
* @param sc
*/
public void start() {
initFactoryList();
// Register factory listener
try {
m_context.addServiceListener(this, m_filterStr);
} catch (InvalidSyntaxException e) {
e.printStackTrace(); // Should not happens
}
// Initialize the instances
if (m_usedRef.size() > 0) {
Set keys = m_usedRef.keySet();
Iterator it = keys.iterator();
if (m_isAggregate) {
while (it.hasNext()) {
ServiceReference ref = (ServiceReference) it.next();
createInstance(ref);
}
} else {
ServiceReference ref = (ServiceReference) it.next();
createInstance(ref);
}
}
m_isValid = isSatisfied();
}
/**
* Stop the service instance.
*/
public void stop() {
m_context.removeServiceListener(this);
Set keys = m_usedRef.keySet();
Iterator it = keys.iterator();
while (it.hasNext()) {
ServiceReference ref = (ServiceReference) it.next();
Object o = m_usedRef.get(ref);
if (o != null) {
((ComponentInstance) o).dispose();
}
}
m_usedRef.clear();
m_isValid = false;
}
/**
* Check if an instance is already created.
* @return true if at least one instance is created.
*/
private boolean isAnInstanceCreated() {
Set keys = m_usedRef.keySet();
Iterator it = keys.iterator();
ServiceReference ref = (ServiceReference) it.next();
Object o = m_usedRef.get(ref);
return o != null;
}
/**
* Create an instance for the given reference.
* @param ref : the service reference to used to create the instance.
*/
private void createInstance(ServiceReference ref) {
try {
Factory factory = (Factory) m_context.getService(ref);
// Add an unique name if not specified.
Properties p = new Properties();
Enumeration kk = m_configuration.keys();
while (kk.hasMoreElements()) {
String k = (String) kk.nextElement();
p.put(k, m_configuration.get(k));
}
ComponentInstance instance = factory.createComponentInstance(p);
m_usedRef.put(ref, instance);
m_context.ungetService(ref);
} catch (Throwable e) {
m_handler.getManager().getFactory().getLogger().log(Logger.ERROR,
"A matching factory (" + ref.getProperty("service.pid") + ") seems to refuse the given configuration : " + e.getMessage());
}
}
/**
* Create an instance for the next available factory.
*/
private void createNextInstance() {
Set keys = m_usedRef.keySet();
Iterator it = keys.iterator();
ServiceReference ref = (ServiceReference) it.next();
try {
Factory factory = (Factory) m_context.getService(ref);
// Add an unique name if not specified.
Properties p = new Properties();
Enumeration kk = m_configuration.keys();
while (kk.hasMoreElements()) {
String k = (String) kk.nextElement();
p.put(k, m_configuration.get(k));
}
ComponentInstance instance = factory.createComponentInstance(p);
m_usedRef.put(ref, instance);
m_context.ungetService(ref);
} catch (UnacceptableConfiguration e) {
m_handler.getManager().getFactory().getLogger().log(Logger.ERROR,
"A matching factory (" + ref.getProperty("service.pid") + ") seems to refuse the given configuration : " + e.getMessage());
}
}
/**
* Kill an instance (if exist).
* If an instance if created by using the given reference, this reference is disposed.
* @param ref : the leaving reference.
*/
private void stopInstance(ServiceReference ref) {
Object o = m_usedRef.get(ref);
if (o != null) {
((ComponentInstance) o).dispose();
}
}
/**
* Initialize the list of available factory.
*/
public void initFactoryList() {
// Initialize factory list
try {
ServiceReference[] refs = m_context.getServiceReferences(Factory.class.getName(), m_filterStr);
if (refs == null) {
return;
}
for (int i = 0; i < refs.length; i++) {
ServiceReference ref = refs[i];
Factory fact = (Factory) m_context.getService(ref);
// Check provided specification & configuration
if (match(fact)) {
m_usedRef.put(ref, null);
}
fact = null;
m_context.ungetService(ref);
}
} catch (InvalidSyntaxException e) {
e.printStackTrace(); // Should not happen
}
}
/**
* Check if the service instance is satisfed.
* @return true if the service instance if satisfied.
*/
public boolean isSatisfied() {
return m_isOptional || m_usedRef.size() > 0;
}
/**
* Does the service instance match with the given factory.
*
* @param fact : the factory to test.
* @return true if the factory match, false otherwise.
*/
private boolean match(Factory fact) {
// Check if the factory can provide the specification
Element[] provides = fact.getDescription().getElements("provides");
for (int i = 0; i < provides.length; i++) {
if (provides[i].getAttribute("specification").equals(m_specification)) {
// Check that the factory needs every properties contained in
// the configuration
Enumeration e = m_configuration.keys();
while (e.hasMoreElements()) {
String k = (String) e.nextElement();
if (!containsProperty(k, fact)) {
return false;
}
}
// Add an unique name if not specified.
Properties p = new Properties();
Enumeration keys = m_configuration.keys();
while (keys.hasMoreElements()) {
String k = (String) keys.nextElement();
p.put(k, m_configuration.get(k));
}
// Check the acceptability.
return fact.isAcceptable(p);
}
}
return false;
}
/**
* Does the factory support the given property ?
*
* @param name : name of the property
* @param factory : factory to test
* @return true if the factory support this property
*/
private boolean containsProperty(String name, Factory factory) {
Element[] props = factory.getDescription().getElements("property");
for (int i = 0; i < props.length; i++) {
if (props[i].getAttribute("name").equalsIgnoreCase(name)) {
return true;
}
}
if (name.equalsIgnoreCase("name")) {
return true;
} // Skip the name property
return false;
}
/**
* Service Listener Implementation.
* @param ev : the service event
* @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
*/
public void serviceChanged(ServiceEvent ev) {
if (ev.getType() == ServiceEvent.REGISTERED) {
// Check the matching
Factory fact = (Factory) m_context.getService(ev.getServiceReference());
if (match(fact)) {
m_usedRef.put(ev.getServiceReference(), null);
if (m_isAggregate) { // Create an instance for the new
// factory
createInstance(ev.getServiceReference());
if (!m_isValid) {
m_isValid = true;
m_handler.validate();
}
} else {
if (!isAnInstanceCreated()) {
createInstance(ev.getServiceReference());
}
if (!m_isValid) {
m_isValid = true;
m_handler.validate();
}
}
}
fact = null;
m_context.ungetService(ev.getServiceReference());
return;
}
if (ev.getType() == ServiceEvent.UNREGISTERING) {
// Remove the reference is contained
Object o = m_usedRef.remove(ev.getServiceReference());
if (o != null) {
stopInstance(ev.getServiceReference());
if (m_usedRef.size() > 0) {
if (!m_isAggregate) {
createNextInstance(); // Create an instance with
// another factory
}
} else { // No more candidate
if (!m_isOptional) {
m_isValid = false;
m_handler.invalidate();
}
}
}
}
}
/**
* Get the required specification.
* @return the required specification.
*/
public String getSpecification() {
return m_specification;
}
public boolean isAggregate() {
return m_isAggregate;
}
public boolean isOptional() {
return m_isOptional;
}
/**
* Get the map of used references [reference, component instance].
* @return the map of used references.
*/
protected Map getUsedReferences() {
return m_usedRef;
}
}