/*******************************************************************************
* Copyright 2014 Miami-Dade County
*
* Licensed 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.sharegov.cirm.owl;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import mjson.Json;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.sharegov.cirm.OWL;
import org.sharegov.cirm.utils.Ref;
/**
* This caches everything because it is serving an application with close-ended set of
* queries/individuals that it asks for. We are not caching operational data that grows
* and grows, we are caching ontological data that kind of grows but not necessarily so much
* and it may even shrink from time to time.
*
* In case this turns out to be a memory hog, we have to implement an eviction policy of
* course.
*
* @author boris
*
*/
public class OWLSerialEntityCache
{
public static final int CACHE_INITIAL_CAPACITY = 2000;
public static final float CACHE_LOAD_FACTOR = 0.5f;
public static final int CACHE_CONCURRENCY_LEVEL = 50;
final ConcurrentHashMap<IRI, Ref<Json>> entityRefs = new ConcurrentHashMap<IRI, Ref<Json>>(CACHE_INITIAL_CAPACITY, CACHE_LOAD_FACTOR, CACHE_CONCURRENCY_LEVEL);
final ConcurrentHashMap<String, Ref<Json>> setRefs = new ConcurrentHashMap<String, Ref<Json>>(CACHE_INITIAL_CAPACITY, CACHE_LOAD_FACTOR, CACHE_CONCURRENCY_LEVEL);
@SuppressWarnings("unchecked")
abstract static class CachedRef<T> implements Ref<T>
{
static final Object nil = new Object();
private T value = (T)nil;
protected abstract T compute();
public void clear()
{
value = (T)nil;
}
public T resolve()
{
if (value == nil)
{
value = compute();
}
return value;
}
}
class IndividualRef extends CachedRef<Json>
{
private IRI iri;
public IndividualRef(IRI iri) { this.iri = iri; }
public synchronized Json compute()
{
return OWL.toJSON(OWL.individual(iri));
}
}
class SetRef extends CachedRef<Json>
{
String dlExpression;
public SetRef(String dlExpression) { this.dlExpression = dlExpression; }
public synchronized Json compute()
{
Json json = Json.array();
for (OWLNamedIndividual ind : OWL.queryIndividuals(dlExpression))
json.add(OWL.toJSON(ind));
return json;
}
}
/**
* Obtain a set of individuals as described by a DL expression.
*
* @param dlExpression
* @return
*/
public Ref<Json> set(String dlExpression)
{
Ref<Json> result = setRefs.get(dlExpression);
if (result == null)
{
result = new SetRef(dlExpression);
Ref<Json> x = setRefs.putIfAbsent(dlExpression, result);
if (x != null)
result = x;
}
return result;
}
/**
* Obtain the JSON serialized form of a given individual in the ontology.
*
* @param iri
* @return
*/
public Ref<Json> individual(IRI iri)
{
Ref<Json> result = entityRefs.get(iri);
if (result == null)
{
result = new IndividualRef(iri);
Ref<Json> x = entityRefs.putIfAbsent(iri, result);
if (x != null)
result = x;
}
return result;
}
public void notify(Set<OWLOntologyChange> changes)
{
clearAll();
}
public void clearAll()
{
entityRefs.clear();
setRefs.clear();
}
}