Merge pull request #20 from kaikreuzer/agbp

Introduced concurrency fixes as done in https://github.com/openhab/op
This commit is contained in:
Kai Kreuzer 2016-02-17 09:47:21 +01:00
commit 49ef22b90c

View File

@ -13,7 +13,6 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
@ -25,11 +24,15 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* <p>This abstract class serves as a basis for implementations of binding providers that retrieve binding * <p>
* information from the items configuration file(s), i.e. they register as {@link BindingConfigReader}s.</p> * This abstract class serves as a basis for implementations of binding providers that retrieve binding
* information from the items configuration file(s), i.e. they register as {@link BindingConfigReader}s.
* </p>
* *
* <p>This class takes care of tracking all changes in the binding config strings and makes sure that all * <p>
* listeners are correctly notified of any change.<p> * This class takes care of tracking all changes in the binding config strings and makes sure that all
* listeners are correctly notified of any change.
* <p>
* *
* @author Kai Kreuzer * @author Kai Kreuzer
* @since 0.6.0 * @since 0.6.0
@ -37,100 +40,116 @@ import org.slf4j.LoggerFactory;
*/ */
public abstract class AbstractGenericBindingProvider implements BindingConfigReader, BindingProvider { public abstract class AbstractGenericBindingProvider implements BindingConfigReader, BindingProvider {
private static final Logger logger = LoggerFactory.getLogger(AbstractGenericBindingProvider.class); private static final Logger logger = LoggerFactory.getLogger(AbstractGenericBindingProvider.class);
private Set<BindingChangeListener> listeners = new CopyOnWriteArraySet<BindingChangeListener>(); private Set<BindingChangeListener> listeners = new CopyOnWriteArraySet<BindingChangeListener>();
/** caches binding configurations. maps itemNames to {@link BindingConfig}s */ /** caches binding configurations. maps itemNames to {@link BindingConfig}s */
protected Map<String, BindingConfig> bindingConfigs = new ConcurrentHashMap<String, BindingConfig>(new WeakHashMap<String, BindingConfig>()); protected Map<String, BindingConfig> bindingConfigs = new ConcurrentHashMap<String, BindingConfig>();
/** /**
* stores information about the context of items. The map has this content * stores information about the context of items. The map has this content
* structure: context -> Set of Items * structure: context -> Set of Items
*/ */
protected Map<String, Set<Item>> contextMap = new ConcurrentHashMap<String, Set<Item>>(); protected Map<String, Set<Item>> contextMap = new ConcurrentHashMap<String, Set<Item>>();
public AbstractGenericBindingProvider() {
super();
}
public AbstractGenericBindingProvider() { /**
super(); * {@inheritDoc}
} */
@Override
public void addBindingChangeListener(BindingChangeListener listener) {
listeners.add(listener);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void addBindingChangeListener(BindingChangeListener listener) { @Override
listeners.add(listener); public void removeBindingChangeListener(BindingChangeListener listener) {
} listeners.remove(listener);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void removeBindingChangeListener(BindingChangeListener listener) { @Override
listeners.remove(listener); public void processBindingConfiguration(String context, Item item, String bindingConfig)
} throws BindingConfigParseException {
if (context == null) {
throw new BindingConfigParseException("null context is not permitted for item " + item.getName());
}
synchronized (contextMap) {
Set<Item> items = contextMap.get(context);
if (items == null) {
items = new HashSet<Item>();
contextMap.put(context, items);
}
items.add(item);
}
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void processBindingConfiguration(String context, Item item, String bindingConfig) throws BindingConfigParseException { @Override
Set<Item> items = contextMap.get(context); public void removeConfigurations(String context) {
if (items==null) { Set<Item> items = null;
items = new HashSet<Item>(); synchronized (contextMap) {
contextMap.put(context, items); items = contextMap.get(context);
} if (items != null) {
contextMap.remove(context);
}
}
if (items != null) {
for (Item item : items) {
// we remove all binding configurations for all items
bindingConfigs.remove(item.getName());
notifyListeners(item);
}
}
}
items.add(item); protected void addBindingConfig(Item item, BindingConfig config) {
} bindingConfigs.put(item.getName(), config);
notifyListeners(item);
}
/** private void notifyListeners(Item item) {
* {@inheritDoc} for (BindingChangeListener listener : listeners) {
*/
public void removeConfigurations(String context) {
Set<Item> items = contextMap.get(context);
if(items!=null) {
for(Item item : items) {
// we remove all binding configurations for all items
bindingConfigs.remove(item.getName());
notifyListeners(item);
}
contextMap.remove(context);
}
}
protected void addBindingConfig(Item item, BindingConfig config) {
bindingConfigs.put(item.getName(), config);
notifyListeners(item);
}
private void notifyListeners(Item item) {
for (BindingChangeListener listener : listeners) {
try { try {
listener.bindingChanged(this, item.getName()); listener.bindingChanged(this, item.getName());
} catch (Exception e) { } catch (Exception e) {
logger.error("Binding " + listener.getClass().getName() + " threw an exception: ", e); logger.error("Binding " + listener.getClass().getName() + " threw an exception: ", e);
} }
} }
} }
/** /**
* @{inheritDoc} * @{inheritDoc}
*/ */
public boolean providesBindingFor(String itemName) { @Override
return bindingConfigs.get(itemName) != null; public boolean providesBindingFor(String itemName) {
} return bindingConfigs.get(itemName) != null;
}
/** /**
* @{inheritDoc} * @{inheritDoc}
*/ */
public boolean providesBinding() { @Override
return !bindingConfigs.isEmpty(); public boolean providesBinding() {
} return !bindingConfigs.isEmpty();
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public Collection<String> getItemNames() { @Override
return new ArrayList<String>(bindingConfigs.keySet()); public Collection<String> getItemNames() {
} return new ArrayList<String>(bindingConfigs.keySet());
}
} }