blob: d7f227aee8dcb0efe3fc0e2bb49252b94988fdad [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.util;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.List;
import org.apache.felix.ipojo.ConfigurationException;
import org.apache.felix.ipojo.ContextListener;
import org.apache.felix.ipojo.ContextSource;
import org.apache.felix.ipojo.composite.CompositeManager;
import org.apache.felix.ipojo.parser.ParseUtils;
import org.apache.felix.ipojo.util.DependencyModel;
import org.apache.felix.ipojo.util.Tracker;
import org.apache.felix.ipojo.util.TrackerCustomizer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
/**
* This class manages context-source management.
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class SourceManager implements ContextListener {
/**
* Source Name service property.
*/
public static final String SOURCE_NAME = "source.name";
/**
* Managed dependency.
*/
private DependencyModel m_dependency;
/**
* List of monitored context sources.
*/
private List/* <ContextSource> */m_sources = new ArrayList(1);
/**
* PRoperties contained in the original filter.
*/
private String[] m_properties;
/**
* Original filter (containing variables).
*/
private String m_filter;
/**
* Bundle context.
*/
private BundleContext m_context;
/**
* Service Tracker List.
*/
private List/*<SourceTracker>*/m_trackers = new ArrayList(1);
/**
* Constructor.
* @param sources : context-source attribute from the dependency metadata
* @param depfilter : original dependency filter
* @param dependency : dependency object
* @param manager : composite manager
* @throws ConfigurationException : the sources are incorrect.
*/
public SourceManager(String sources, String depfilter, DependencyModel dependency, CompositeManager manager) throws ConfigurationException {
m_filter = depfilter;
m_properties = getProperties(depfilter);
m_dependency = dependency;
m_context = manager.getGlobalContext();
if (manager.getParentServiceContext() == null) {
// The parent is the global context
parseSources(sources, manager.getGlobalContext(), manager.getGlobalContext(), manager.getServiceContext());
} else {
parseSources(sources, manager.getGlobalContext(), manager.getParentServiceContext(), manager.getServiceContext());
}
}
/**
* Start the context management.
*/
public void start() {
for (int i = 0; i < m_trackers.size(); i++) {
((SourceTracker) m_trackers.get(i)).open();
}
computeFilter();
}
/**
* Stop the context management.
*/
public void stop() {
for (int i = 0; i < m_trackers.size(); i++) {
((SourceTracker) m_trackers.get(i)).close();
}
setFilter(m_filter);
m_sources.clear();
}
/**
* Get the state of this source manager.
* @return the state of this source manager.
*/
public int getState() {
if (m_sources.isEmpty()) {
return DependencyModel.UNRESOLVED;
} else {
return DependencyModel.RESOLVED;
}
}
/**
* Set the filter of the managed dependency.
* @param filter : the new filter to apply
*/
private void setFilter(String filter) {
if (!filter.equals(m_dependency.getFilter())) {
// Reconfigure
try {
m_dependency.setFilter(m_context.createFilter(filter));
} catch (InvalidSyntaxException e) {
throw new IllegalStateException("A context filter is invalid : " + filter);
}
}
}
/**
* Compute the new filter.
*/
private void computeFilter() {
String fil = m_filter;
synchronized (this) {
for (int i = 0; i < m_sources.size(); i++) {
Dictionary props = ((ContextSource) m_sources.get(i)).getContext();
fil = substitute(fil, props); //NOPMD
}
}
if (!fil.equals(m_dependency.getFilter())) {
setFilter(fil);
}
}
/**
* This method substitute ${var} substring by values stored in a map.
* @param str : string with variables
* @param values : dictionary containing the variable name and the value.
* @return resulted string
*/
public static String substitute(String str, Dictionary values) {
int len = str.length();
StringBuffer buffer = new StringBuffer(len);
int prev = 0;
int start = str.indexOf("${");
int end = str.indexOf('}', start);
while (start != -1 && end != -1) {
String key = str.substring(start + 2, end);
Object value = values.get(key);
if (value == null) {
buffer.append(str.substring(prev, end + 1));
} else {
buffer.append(str.substring(prev, start));
buffer.append(value);
}
prev = end + 1;
if (prev >= str.length()) {
break;
}
start = str.indexOf("${", prev);
if (start != -1) {
end = str.indexOf('}', start);
}
}
buffer.append(str.substring(prev));
return buffer.toString();
}
/**
* Compute the properties (${name}) from the given filter.
* @param str : string form of the filter.
* @return the list of found properties.
*/
public static String[] getProperties(String str) {
List list = new ArrayList();
int prev = 0;
int start = str.indexOf("${");
int end = str.indexOf('}', start);
while (start != -1 && end != -1) {
String key = str.substring(start + 2, end);
list.add(key);
prev = end + 1;
if (prev >= str.length()) {
break;
}
start = str.indexOf("${", prev);
if (start != -1) {
end = str.indexOf('}', start);
}
}
return (String[]) list.toArray(new String[list.size()]);
}
/**
* A context source has modified a monitored property.
* @param source : source
* @param property : modified property
* @param value : new value.
* @see org.apache.felix.ipojo.ContextListener#update(org.apache.felix.ipojo.ContextSource, java.lang.String, java.lang.Object)
*/
public synchronized void update(ContextSource source, String property, Object value) {
computeFilter();
}
/**
* Parse the context-source attribute in order to create source tracker object.
* @param sourceAtt : context-source attribute.
* @param global : global bundle context.
* @param parent : parent bundle context.
* @param local : local bundle context.
* @throws ConfigurationException : the context-source attribute is invalid.
*/
private void parseSources(String sourceAtt, BundleContext global, BundleContext parent, BundleContext local) throws ConfigurationException {
String[] sources = ParseUtils.split(sourceAtt, ",");
for (int i = 0; i < sources.length; i++) {
String[] srcs = ParseUtils.split(sources[i], ":");
if (srcs.length == 1) {
// No prefix use local. //TODO choose default case.
SourceTracker tracker = new SourceTracker(srcs[0], local);
m_trackers.add(tracker);
} else if (srcs.length == 2) {
// According to prefix add the source in the good list.
if (srcs[0].equalsIgnoreCase("parent")) {
SourceTracker tracker = new SourceTracker(srcs[1], parent);
m_trackers.add(tracker);
} else if (srcs[0].equalsIgnoreCase("local")) {
SourceTracker tracker = new SourceTracker(srcs[1], local);
m_trackers.add(tracker);
} else if (srcs[0].equalsIgnoreCase("global")) {
SourceTracker tracker = new SourceTracker(srcs[1], global);
m_trackers.add(tracker);
} else {
throw new ConfigurationException("Unknowns context scope : " + srcs[0]);
}
} else {
throw new ConfigurationException("Malformed context source : " + sources[i]);
}
}
}
/**
* A context source appears.
* @param source : new context source.
*/
private void addContextSource(ContextSource source) {
m_sources.add(source);
computeFilter();
source.registerContextListener(this, m_properties);
}
/**
* A context source disappears.
* @param source : leaving context source.
*/
private void removeContextSource(ContextSource source) {
m_sources.remove(source);
computeFilter();
}
private class SourceTracker implements TrackerCustomizer {
/**
* Service tracker.
*/
private Tracker m_tracker;
/**
* Constructor.
* @param name : name of the required context-source.
* @param countext : bundle context to use.
* @throws ConfigurationException : the context-source name is invalid.
*/
public SourceTracker(String name, BundleContext countext) throws ConfigurationException {
String fil = "(&(" + Constants.OBJECTCLASS + "=" + ContextSource.class.getName() + ")(" + SOURCE_NAME + "=" + name + "))";
try {
Filter filter = countext.createFilter(fil);
m_tracker = new Tracker(countext, filter, this);
} catch (InvalidSyntaxException e) {
throw new ConfigurationException("A Context source filter is invalid " + fil + " : " + e.getMessage());
}
}
/**
* Open the tracker.
*/
public void open() {
m_tracker.open();
}
/**
* Close the tracker.
*/
public void close() {
m_tracker.close();
}
/**
* A new context-source was added.
* This method inject the context-source object in the source manager.
* @param reference : service reference.
* @see org.apache.felix.ipojo.util.TrackerCustomizer#addedService(org.osgi.framework.ServiceReference)
*/
public void addedService(ServiceReference reference) {
addContextSource((ContextSource) m_tracker.getService(reference));
}
/**
* A new context-source is adding in the tracker..
* @param reference : service reference
* @return true.
* @see org.apache.felix.ipojo.util.TrackerCustomizer#addingService(org.osgi.framework.ServiceReference)
*/
public boolean addingService(ServiceReference reference) {
return true;
}
/**
* A used context-source is modified.
* @param reference : service reference.
* @param service : service object.
* @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
*/
public void modifiedService(ServiceReference reference, Object service) {
// Nothing to do.
}
/**
* A used context-source disappears.
* This method notify the Source Manager in order to manage this departure.
* @param reference : service reference.
* @param service : service object.
* @see org.apache.felix.ipojo.util.TrackerCustomizer#modifiedService(org.osgi.framework.ServiceReference, java.lang.Object)
*/
public void removedService(ServiceReference reference, Object service) {
removeContextSource((ContextSource) service);
}
}
}