blob: 6a0a5b1c2fe371a9602a02f6e9db6cb90a234a58 [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.provides;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.List;
import java.util.Properties;
import org.apache.felix.ipojo.ServiceContext;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/**
* Export an service from the scope to the parent context.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class ServiceExporter implements ServiceListener {
/**
* Destination context.
*/
private BundleContext m_destination;
/**
* Origin context.
*/
private ServiceContext m_origin;
/**
* Exported specification.
*/
private String m_specification;
/**
* LDAP filter filtering internal provider.
*/
private Filter m_filter;
/**
* String form of the LDAP filter.
*/
private String m_filterStr;
/**
* Should be exported several providers.
*/
private boolean m_aggregate = false;
/**
* Is this exports optional?
*/
private boolean m_optional = false;
/**
* Reference on the provided service.
*/
private ProvidedService m_ps;
/**
* Is the export valid?
*/
private boolean m_isValid;
private class Record {
/**
* Internal Reference.
*/
private ServiceReference m_ref;
/**
* External Registration.
*/
private ServiceRegistration m_reg;
/**
* Exposed object.
*/
private Object m_svcObject;
}
/**
* List of managed records.
*/
private List/* <Record> */m_records = new ArrayList()/* <Record> */;
/**
* Constructor.
*
* @param specification : exported service specification.
* @param filter : LDAP filter
* @param multiple : is the export an aggregate export?
* @param optional : is the export optional?
* @param from : internal service context
* @param to : external bundle context
* @param exp : handler
*/
public ServiceExporter(String specification, String filter, boolean multiple, boolean optional, ServiceContext from, BundleContext to, ProvidedService exp) {
this.m_destination = to;
this.m_origin = from;
this.m_ps = exp;
try {
this.m_filter = to.createFilter(filter);
} catch (InvalidSyntaxException e) {
e.printStackTrace();
return;
}
this.m_aggregate = multiple;
this.m_specification = specification;
this.m_optional = optional;
}
/**
* Start method.
* Start the export and the provider tracking.
*/
public synchronized void start() {
try {
ServiceReference[] refs = m_origin.getServiceReferences(m_specification, null);
if (refs != null) {
for (int i = 0; i < refs.length; i++) {
if (m_filter.match(refs[i])) {
Record rec = new Record();
rec.m_ref = refs[i];
m_records.add(rec);
}
}
}
} catch (InvalidSyntaxException e) {
e.printStackTrace();
}
// Publish available services
if (m_records.size() > 0) {
if (m_aggregate) {
for (int i = 0; i < m_records.size(); i++) {
Record rec = (Record) m_records.get(i);
rec.m_svcObject = m_origin.getService(rec.m_ref);
rec.m_reg = m_destination.registerService(m_specification, rec.m_svcObject, getProps(rec.m_ref));
}
} else {
Record rec = (Record) m_records.get(0);
if (rec.m_reg == null) {
rec.m_svcObject = m_origin.getService(rec.m_ref);
rec.m_reg = m_destination.registerService(m_specification, rec.m_svcObject, getProps(rec.m_ref));
}
}
}
// Register service listener
try {
m_origin.addServiceListener(this, "(" + Constants.OBJECTCLASS + "=" + m_specification + ")");
} catch (InvalidSyntaxException e) {
e.printStackTrace();
}
m_isValid = isSatisfied();
}
/**
* Transform service reference property in a dictionary.
* Service.PID and Factory.PID are injected too.
* @param ref : the service reference.
* @return the dictionary containing all property of the given service reference.
*/
private Dictionary getProps(ServiceReference ref) {
Properties prop = new Properties();
String[] keys = ref.getPropertyKeys();
for (int i = 0; i < keys.length; i++) {
prop.put(keys[i], ref.getProperty(keys[i]));
}
prop.put(Constants.SERVICE_PID, m_ps.getManager().getInstanceName());
prop.put("factory.pid", m_ps.getManager().getFactory().getName());
return prop;
}
/**
* Stop method.
* Remove the service listener and unregister all exported service.
*/
public synchronized void stop() {
m_origin.removeServiceListener(this);
for (int i = 0; i < m_records.size(); i++) {
Record rec = (Record) m_records.get(i);
rec.m_svcObject = null;
if (rec.m_reg != null) {
rec.m_reg.unregister();
m_origin.ungetService(rec.m_ref);
rec.m_ref = null;
}
}
m_records.clear();
}
/**
* Check exporter validity.
* @return true if the exports is optional, or a service is really exported
*/
public boolean isSatisfied() {
return m_optional || m_records.size() > 0;
}
/**
* Check if a service is published.
* @return true if at least one service is published by this handler
*/
public boolean isPublishing() {
return m_records.size() > 0;
}
/**
* Get the list of records using the given reference.
* @param ref : the service reference
* @return the list of records using the given reference, empty if no record used this reference
*/
private List/* <Record> */getRecordsByRef(ServiceReference ref) {
List l = new ArrayList();
for (int i = 0; i < m_records.size(); i++) {
Record rec = (Record) m_records.get(i);
if (rec.m_ref == ref) {
l.add(rec);
}
}
return l;
}
/**
* 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) {
arrivalManagement(ev.getServiceReference());
}
if (ev.getType() == ServiceEvent.UNREGISTERING) {
departureManagement(ev.getServiceReference());
}
if (ev.getType() == ServiceEvent.MODIFIED) {
if (m_filter.match(ev.getServiceReference())) {
// Test if the ref is always matching with the filter
List l = getRecordsByRef(ev.getServiceReference());
if (l.size() > 0) { // The ref is already contained => update
// the properties
for (int i = 0; i < l.size(); i++) { // Stop the implied
// record
Record rec = (Record) l.get(i);
if (rec.m_reg != null) {
rec.m_reg.setProperties(getProps(rec.m_ref));
}
}
} else { // it is a new mathcing service => add it
arrivalManagement(ev.getServiceReference());
}
} else {
List l = getRecordsByRef(ev.getServiceReference());
if (l.size() > 0) { // The ref is already contained => the
// service does no more match
departureManagement(ev.getServiceReference());
}
}
}
}
/**
* Manage the arrival of a service.
* @param ref : the new service reference.
*/
private synchronized void arrivalManagement(ServiceReference ref) {
// Check if the new service match
if (m_filter.match(ref)) {
// Add it to the record list
Record rec = new Record();
rec.m_ref = ref;
m_records.add(rec);
// Publishing ?
if (m_records.size() == 1 || m_aggregate) { // If the service is the
// first one, or if it
// is a multiple imports
rec.m_svcObject = m_origin.getService(rec.m_ref);
rec.m_reg = m_destination.registerService(m_specification, rec.m_svcObject, getProps(rec.m_ref));
}
// Compute the new state
if (!m_isValid && isSatisfied()) {
m_isValid = true;
m_ps.validating(this);
}
}
}
/**
* Manage the departure of a service.
* @param ref : the new service reference.
*/
private synchronized void departureManagement(ServiceReference ref) {
List l = getRecordsByRef(ref);
for (int i = 0; i < l.size(); i++) { // Stop the implied record
Record rec = (Record) l.get(i);
if (rec.m_reg != null) {
rec.m_svcObject = null;
rec.m_reg.unregister();
rec.m_reg = null;
m_origin.ungetService(rec.m_ref);
}
}
m_records.removeAll(l);
// Check the validity & if we need to reimport the service
if (m_records.size() > 0) {
// There is other available services
if (!m_aggregate) { // Import the next one
Record rec = (Record) m_records.get(0);
if (rec.m_svcObject == null) { // It is the first service who
// disappears - create the next
// one
rec.m_svcObject = m_origin.getService(rec.m_ref);
rec.m_reg = m_destination.registerService(m_specification, rec.m_svcObject, getProps(rec.m_ref));
}
}
} else {
if (!m_optional) {
m_isValid = false;
m_ps.invalidating(this);
}
}
}
protected String getSpecification() {
return m_specification;
}
public String getFilter() {
return m_filterStr;
}
}