package eu.etaxonomy.cdm.api.service;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
-import org.apache.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import eu.etaxonomy.cdm.model.common.CdmBase;
import eu.etaxonomy.cdm.model.metadata.CdmPreference;
import eu.etaxonomy.cdm.model.metadata.CdmPreference.PrefKey;
-import eu.etaxonomy.cdm.model.metadata.PreferencePredicate;
+import eu.etaxonomy.cdm.model.metadata.IPreferencePredicate;
+import eu.etaxonomy.cdm.model.metadata.PreferenceResolver;
import eu.etaxonomy.cdm.model.metadata.PreferenceSubject;
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
import eu.etaxonomy.cdm.persistence.dao.common.IPreferenceDao;
/**
+ * Service to store and access {@link CdmPreference cdm preferences}.
+ * On how to use cdm preferences see the documentation there.
+ *
* @author a.mueller
* @since 2013-09-09
*/
public class PreferenceServiceImpl implements IPreferenceService {
@SuppressWarnings("unused")
- private static final Logger logger = Logger.getLogger(PreferenceServiceImpl.class);
+ private static final Logger logger = LogManager.getLogger();
@Autowired
private IPreferenceDao dao;
- @Override
- public CdmPreference find(PrefKey key) {
- List<PrefKey> keys = new ArrayList<>();
- keys.add(key);
-// while(key.) TODO
+ private Map<String, CdmPreference> cache = new ConcurrentHashMap<>();
+
+ private boolean cacheIsComplete = false;
- return dao.get(key);
+ private boolean cacheIsLocked = false;
+
+ @Override
+ public CdmPreference findExact(PrefKey key) {
+ String cacheKey = cacheKey(key);
+ return fromCacheGet(key, cacheKey);
}
- /**
- * {@inheritDoc}
- */
@Override
- public CdmPreference findDatabase(PreferencePredicate predicate){
+ public CdmPreference find(PrefKey key) {
+ CdmPreference pref = PreferenceResolver.resolve(list(), key);
+ return pref;
+ }
+
+ @Override
+ public CdmPreference findDatabase(IPreferencePredicate<?> predicate){
PrefKey key = CdmPreference.NewKey(PreferenceSubject.NewDatabaseInstance(), predicate);
return find(key);
}
- /**
- * {@inheritDoc}
- */
@Override
- public CdmPreference findVaadin(PreferencePredicate predicate){
+ public CdmPreference findVaadin(IPreferencePredicate<?> predicate){
PrefKey key = CdmPreference.NewKey(PreferenceSubject.NewVaadinInstance(), predicate);
return find(key);
}
@Override
- public CdmPreference findTaxEditor(PreferencePredicate predicate){
+ public CdmPreference findTaxEditor(IPreferencePredicate<?> predicate){
PrefKey key = CdmPreference.NewKey(PreferenceSubject.NewTaxEditorInstance(), predicate);
return find(key);
}
-
@Override
@Transactional(readOnly = false)
public void set(CdmPreference preference) {
dao.set(preference);
+ cachePut(preference);
}
- @Override
+ @Override
+ @Transactional(readOnly = false)
+ public void remove(PrefKey key) {
+ dao.remove(key);
+ removeFromCache(key);
+ }
+
+ @Override
public long count() {
return dao.count();
}
@Override
public List<CdmPreference> list() {
- return dao.list();
+ if(!cacheIsComplete) {
+ cacheFullUpdate();
+ }
+ return new ArrayList<>(cacheValues());
}
@Override
- public Object find(TaxonNode taxonNode, String predicate) {
- return dao.find(taxonNode, predicate);
+ public List<CdmPreference> list(IPreferencePredicate<?> predicate) {
+ // using the cache for this method makes not much sense
+ return dao.list(predicate);
}
@Override
- public CdmPreference find(TaxonNode taxonNode, PreferencePredicate predicate){
- return dao.find(taxonNode, predicate.getKey());
+ public CdmPreference find(TaxonNode taxonNode, String predicate) {
+
+ return dao.find(taxonNode, predicate);
+// caching of taxon node related prefs disabled, see https://dev.e-taxonomy.eu/redmine/issues/9152#note-8
+// code has been left here in case we decide later for solution 2 which has been suggested also in comment 5
+// String cacheKey = cacheKey(taxonNode, predicate);
+// return fromCacheOrFind(taxonNode, predicate, cacheKey);
}
+ @Override
+ public CdmPreference find(TaxonNode taxonNode, IPreferencePredicate<?> predicate){
+ return find(taxonNode, predicate.getKey());
+ }
// ********************** NOT YET HANDLED *******************/
-
@Override
public List<CdmPreference> list(String subject, String predicate) {
//FIXME
return dao.find(taxonNode, predicate);
}
- /**
- * @param taxonNodeRelatedCdmBase
- * @return
- */
private TaxonNode mapToTaxonNode(CdmBase taxonNodeRelatedCdmBase) {
if (taxonNodeRelatedCdmBase == null){
return null;
}
}
+ // ====================== Cache methods ======================= //
-// @Override
-// public String setCdmPrefs(CdmBase cdmBase, String predicate, String value) {
-// // TODO Auto-generated method stub
-// return null;
-// }
+ /**
+ * Concatenates subject and predicate as key for the cache map
+ */
+ private String cacheKey(PrefKey key) {
+ return key.getSubject() + "@" + key.getPredicate();
+ }
+
+ /**
+ * @deprecated caching of taxon node related prefs disabled, see https://dev.e-taxonomy.eu/redmine/issues/9152#note-8
+ * code has been left here in case we decide later for solution 2 which has been suggested also in comment 5
+ */
+ @Deprecated
+ private String cacheKey(TaxonNode taxonNode, String predicate) {
+ return taxonNode.treeIndex() + predicate;
+ }
+
+
+ // --------------- non locking cache read methods --------------- //
+ protected Collection<CdmPreference> cacheValues() {
+ waitForCache();
+ return cache.values();
+ }
+
+ protected CdmPreference fromCacheGet(PrefKey key, String cacheKey) {
+ waitForCache();
+ return cache.computeIfAbsent(cacheKey, k -> dao.get(key));
+ }
+
+
+ /**
+ * @deprecated caching of taxon node related prefs disabled, see https://dev.e-taxonomy.eu/redmine/issues/9152#note-8
+ * code has been left here in case we decide later for solution 2 which has been suggested also in comment 5
+ */
+ @Deprecated
+ protected CdmPreference fromCacheOrFind(TaxonNode taxonNode, String predicate, String cacheKey) {
+ waitForCache();
+ return cache.computeIfAbsent(cacheKey, k -> dao.find(taxonNode, predicate));
+ }
+
+ // --------------- cache locking methods --------------- //
+
+ protected void cachePut(CdmPreference preference) {
+ waitForCache();
+ cacheIsLocked = true;
+ cache.put(cacheKey(preference.getKey()), preference);
+ cacheIsLocked = false;
+ }
+
+
+ protected void removeFromCache(PrefKey key) {
+ waitForCache();
+ cacheIsLocked = true;
+ cache.remove(cacheKey(key));
+ cacheIsLocked = false;
+ }
+
+ protected void cacheFullUpdate() {
+ waitForCache();
+ cacheIsLocked = true;
+ cache.clear();
+ for(CdmPreference pref : dao.list()){
+ cache.put(cacheKey(pref.getKey()), pref);
+ }
+ cacheIsComplete = true;
+ cacheIsLocked = false;
+ }
+
+ protected void waitForCache() {
+ while(cacheIsLocked) {
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ // just keep on sleeping, we may improve this later on
+ }
+ }
+ }
}