/*
* 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.clerezza.rdf.core.access;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.clerezza.rdf.core.Graph;
import org.apache.clerezza.rdf.core.MGraph;
import org.apache.clerezza.rdf.core.TripleCollection;
import org.apache.clerezza.rdf.core.UriRef;
import org.apache.clerezza.rdf.core.sparql.QueryEngine;
/**
* This makes a set of WeightedTcProvider appear as one TcProvider. It delegates
* requests to the WeightedTcProvider with the highest Weight
*
* @author reto
*/
public class TcProviderMultiplexer implements TcProvider {
protected SortedSet<WeightedTcProvider> providerList = new TreeSet<WeightedTcProvider>(
new WeightedProviderComparator());
/**
* Mapping to LockableMGraph's and ServiceRegistration using their URI's as key.
* Makes sure that per URI only one instance of the LockableMGraph is used,
* otherwise the locks in the <code>LockableMGraph</code>s would have no effect
* between different instances and concurrency issues could occur.
*/
private Map<UriRef, MGraphHolder> mGraphCache = Collections.synchronizedMap(new HashMap<UriRef, MGraphHolder>());
/**
* Registers a provider
*
* @param provider
* the provider to be registered
*/
public void addWeightedTcProvider(WeightedTcProvider provider) {
providerList.add(provider);
updateLockableMGraphCache(provider, true);
}
/**
* Unregister a provider
*
* @param provider
* the provider to be deregistered
*/
public void removeWeightedTcProvider(
WeightedTcProvider provider) {
providerList.remove(provider);
updateLockableMGraphCache(provider, false);
}
/**
* subclasses overwrite this method to be notified when a new
* Graph is available (either because it has been created or being
* provided by a newly added WeightedTcProvider). The default implementation
* does nothing.
*
* @param name
*/
protected void graphAppears(UriRef name) {
}
/**
* subclasses overwrite this method to be notified when a new
* MGraph is available (either because it has been created or being
* provided by a newly added WeightedTcProvider). The default implementation
* does nothing.
*
* @param name
*/
protected void mGraphAppears(UriRef name) {
}
/**
* subclasses overwrite this method to be notified whenTripleCollection is
* no longer available (either because it has been deleted or bacause its
* WeightedTcProvider was removed). The default implementation does nothing.
*
* for implementational reasons even for name of TripleCollection not
* previously registered.
*
* @param name
*/
protected void tcDisappears(UriRef name) {
}
/**
* Updates the lockableMGraphCache AFTER a new <code>provider</code> was
* bound or unbound.
* This method also takes care of registering and unregistering
* provided triple collections as services based on the weight of
* all affected providers.
*
* @param provider
* the provider that was added or removed
* @param providerAdded
* <code>boolean</code> that should be set as <code>true</code>
* if <code>provider</code> was added to
* <code>org.apache.clerezza.rdf.core.TcManager.providerList</code>
* otherwise <code>false</code>
*/
private void updateLockableMGraphCache(WeightedTcProvider provider,
boolean providerAdded) {
Set<UriRef> uriSet = provider.listTripleCollections();
if (!(uriSet == null || uriSet.isEmpty())) {
if (providerAdded) {
weightedProviderAdded(provider, uriSet);
} else {
weightedProviderRemoved(provider, uriSet);
}
}
}
private void weightedProviderAdded(WeightedTcProvider newProvider,
Set<UriRef> newProvidedUris) {
Set<WeightedTcProvider> lowerWeightedProviderList = getLowerWeightedProvider(newProvider);
for (UriRef name : newProvidedUris) {
final MGraphHolder holder = mGraphCache.get(name);
if ((holder != null) && (holder.getWeightedTcProvider() != null)) {
if (lowerWeightedProviderList.contains(holder.getWeightedTcProvider())) {
tcDisappears(name);
mGraphCache.remove(name);
} else {
continue;
}
}
TripleCollection triples = newProvider.getTriples(name);
if (triples instanceof MGraph) {
mGraphCache.put(name, new MGraphHolder(newProvider, ensureLockable((MGraph)triples)));
mGraphAppears(name);
} else {
graphAppears(name);
}
}
}
private Set<WeightedTcProvider> getLowerWeightedProvider(
WeightedTcProvider newProvider) {
boolean referenceProviderPassed = false;
Set<WeightedTcProvider> lowerWeightedProviderList = new HashSet<WeightedTcProvider>();
for (WeightedTcProvider weightedProvider : providerList) {
if (referenceProviderPassed) {
lowerWeightedProviderList.add(weightedProvider);
} else if (newProvider.equals(weightedProvider)) {
referenceProviderPassed = true;
}
}
return lowerWeightedProviderList;
}
private void weightedProviderRemoved(WeightedTcProvider oldProvider,
Set<UriRef> oldProvidedUris) {
for (UriRef name : oldProvidedUris) {
final MGraphHolder holder = mGraphCache.get(name);
if ((holder != null) && (holder.getWeightedTcProvider() != null)
&& holder.getWeightedTcProvider().equals(oldProvider)) {
tcDisappears(name);
mGraphCache.remove(name);
// check if another WeightedTcProvider has the TripleCollection.
// And if so register as service.
for (WeightedTcProvider provider : providerList) {
try {
TripleCollection triples = provider.getTriples(name);
if (triples instanceof MGraph) {
mGraphCache.put(name, new MGraphHolder(provider, ensureLockable((MGraph)triples)));
mGraphAppears(name);
} else {
graphAppears(name);
}
break;
} catch (NoSuchEntityException e) {
// continue;
}
}
}
}
}
@Override
public Graph getGraph(UriRef name) throws NoSuchEntityException {
for (TcProvider provider : providerList) {
try {
return provider.getGraph(name);
} catch (NoSuchEntityException e) {
//we do nothing and try our luck with the next provider
} catch (IllegalArgumentException e) {
//we do nothing and try our luck with the next provider
}
}
throw new NoSuchEntityException(name);
}
@Override
public LockableMGraph getMGraph(UriRef name)
throws NoSuchEntityException {
LockableMGraph result = getMGraphFromCache(name);
if (result == null) {
synchronized (this) {
result = getMGraphFromCache(name);
if (result == null) {
result = getUnsecuredMGraphAndAddToCache(name);
}
}
}
return result;
}
private LockableMGraph getMGraphFromCache(UriRef name) {
MGraphHolder holder = mGraphCache.get(name);
if (holder == null) {
return null;
}
return holder.getMGraph();
}
private LockableMGraph getUnsecuredMGraphAndAddToCache(UriRef name)
throws NoSuchEntityException {
for (WeightedTcProvider provider : providerList) {
try {
MGraph providedMGraph = provider.getMGraph(name);
LockableMGraph result = ensureLockable(providedMGraph);
MGraphHolder holder = mGraphCache.get(name);
mGraphCache.put(name, new MGraphHolder(
provider, result));
return result;
} catch (NoSuchEntityException e) {
//we do nothing and try our luck with the next provider
} catch (IllegalArgumentException e) {
//we do nothing and try our luck with the next provider
}
}
throw new NoSuchEntityException(name);
}
@Override
public TripleCollection getTriples(UriRef name)
throws NoSuchEntityException {
TripleCollection result;
for (WeightedTcProvider provider : providerList) {
try {
result = provider.getTriples(name);
if (!(result instanceof MGraph)) {
return result;
} else {
// This is to ensure the MGraph gets added to the cache
return getMGraph(name);
}
} catch (NoSuchEntityException e) {
//we do nothing and try our luck with the next provider
} catch (IllegalArgumentException e) {
//we do nothing and try our luck with the next provider
}
}
throw new NoSuchEntityException(name);
}
@Override
public LockableMGraph createMGraph(UriRef name)
throws UnsupportedOperationException {
for (WeightedTcProvider provider : providerList) {
try {
MGraph providedMGraph = provider.createMGraph(name);
LockableMGraph result;
if (providedMGraph instanceof LockableMGraph) {
result = (LockableMGraph) providedMGraph;
} else {
result = new LockableMGraphWrapper(providedMGraph);
}
// unregisters a possible Graph or MGraph service under this name
// provided by a WeightedTcProvider with a lower weight.
tcDisappears(name);
mGraphCache.put(name, new MGraphHolder(provider, null));
mGraphAppears(name);
return result;
} catch (UnsupportedOperationException e) {
//we do nothing and try our luck with the next provider
} catch (IllegalArgumentException e) {
//we do nothing and try our luck with the next provider
}
}
throw new UnsupportedOperationException(
"No provider could create MGraph.");
}
@Override
public Graph createGraph(UriRef name, TripleCollection triples) {
for (WeightedTcProvider provider : providerList) {
try {
Graph result = provider.createGraph(name, triples);
// unregisters a possible Graph or MGraph service under this name
// provided by a WeightedTcProvider with a lower weight.
tcDisappears(name);
mGraphCache.put(name, new MGraphHolder(provider, null));
graphAppears(name);
return result;
} catch (UnsupportedOperationException e) {
//we do nothing and try our luck with the next provider
} catch (IllegalArgumentException e) {
//we do nothing and try our luck with the next provider
}
}
throw new UnsupportedOperationException(
"No provider could create Graph.");
}
@Override
public void deleteTripleCollection(UriRef name) {
for (TcProvider provider : providerList) {
try {
provider.deleteTripleCollection(name);
final MGraphHolder holder = mGraphCache.get(name);
if ((holder != null)
&& (holder.getWeightedTcProvider() != null)
&& holder.getWeightedTcProvider().equals(provider)) {
tcDisappears(name);
mGraphCache.remove(name);
}
return;
} catch (UnsupportedOperationException e) {
// we do nothing and try our luck with the next provider
} catch (NoSuchEntityException e) {
//we do nothing and try our luck with the next provider
} catch (IllegalArgumentException e) {
//we do nothing and try our luck with the next provider
}
}
// this throws a NoSuchEntityException if the graph doesn't exist
getTriples(name);
// the entity exists but cannot be deleted
throw new UnsupportedOperationException(
"No provider could delete the entity.");
}
@Override
public Set<UriRef> getNames(Graph graph) {
Set<UriRef> result = new HashSet<UriRef>();
for (TcProvider provider : providerList) {
result.addAll(provider.getNames(graph));
}
return result;
}
@Override
public Set<UriRef> listGraphs() {
Set<UriRef> result = new HashSet<UriRef>();
for (TcProvider provider : providerList) {
result.addAll(provider.listGraphs());
}
return result;
}
@Override
public Set<UriRef> listMGraphs() {
Set<UriRef> result = new HashSet<UriRef>();
for (TcProvider provider : providerList) {
result.addAll(provider.listMGraphs());
}
return result;
}
@Override
public Set<UriRef> listTripleCollections() {
Set<UriRef> result = new HashSet<UriRef>();
for (TcProvider provider : providerList) {
result.addAll(provider.listTripleCollections());
}
return result;
}
private LockableMGraph ensureLockable(MGraph providedMGraph) {
LockableMGraph result;
if (providedMGraph instanceof LockableMGraph) {
result = (LockableMGraph) providedMGraph;
} else {
result = new LockableMGraphWrapper(providedMGraph);
}
return result;
}
/**
* Contains an unsecured LockableMGraph, a ServiceRegistration and
* the WeightedTcProvider that generated the graph
*/
private static class MGraphHolder {
private WeightedTcProvider tcProvider;
private WeakReference<LockableMGraph> mGraphReference;
MGraphHolder(WeightedTcProvider tcProvider, LockableMGraph mGraph) {
this.tcProvider = tcProvider;
this.mGraphReference = new WeakReference<LockableMGraph>(mGraph);
}
LockableMGraph getMGraph() {
return this.mGraphReference.get();
}
WeightedTcProvider getWeightedTcProvider() {
return this.tcProvider;
}
}
//methods for debuging / monitoring
public SortedSet<WeightedTcProvider> getProviderList() {
return providerList;
}
}