Merge branch 'release/5.44.0'
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / PreferenceServiceImpl.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.api.service;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.concurrent.ConcurrentHashMap;
17
18 import org.apache.logging.log4j.LogManager;
19 import org.apache.logging.log4j.Logger;
20 import org.springframework.beans.factory.annotation.Autowired;
21 import org.springframework.stereotype.Service;
22 import org.springframework.transaction.annotation.Transactional;
23
24 import eu.etaxonomy.cdm.model.common.CdmBase;
25 import eu.etaxonomy.cdm.model.metadata.CdmPreference;
26 import eu.etaxonomy.cdm.model.metadata.CdmPreference.PrefKey;
27 import eu.etaxonomy.cdm.model.metadata.IPreferencePredicate;
28 import eu.etaxonomy.cdm.model.metadata.PreferenceResolver;
29 import eu.etaxonomy.cdm.model.metadata.PreferenceSubject;
30 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
31 import eu.etaxonomy.cdm.persistence.dao.common.IPreferenceDao;
32
33 /**
34 * Service to store and access {@link CdmPreference cdm preferences}.
35 * On how to use cdm preferences see the documentation there.
36 *
37 * @author a.mueller
38 * @since 2013-09-09
39 */
40 @Service
41 @Transactional(readOnly = true)
42 public class PreferenceServiceImpl implements IPreferenceService {
43
44 @SuppressWarnings("unused")
45 private static final Logger logger = LogManager.getLogger();
46
47 @Autowired
48 private IPreferenceDao dao;
49
50 private Map<String, CdmPreference> cache = new ConcurrentHashMap<>();
51
52 private boolean cacheIsComplete = false;
53
54 private boolean cacheIsLocked = false;
55
56 @Override
57 public CdmPreference findExact(PrefKey key) {
58 String cacheKey = cacheKey(key);
59 return fromCacheGet(key, cacheKey);
60 }
61
62 @Override
63 public CdmPreference find(PrefKey key) {
64 CdmPreference pref = PreferenceResolver.resolve(list(), key);
65 return pref;
66 }
67
68 @Override
69 public CdmPreference findDatabase(IPreferencePredicate<?> predicate){
70 PrefKey key = CdmPreference.NewKey(PreferenceSubject.NewDatabaseInstance(), predicate);
71 return find(key);
72 }
73
74 @Override
75 public CdmPreference findVaadin(IPreferencePredicate<?> predicate){
76 PrefKey key = CdmPreference.NewKey(PreferenceSubject.NewVaadinInstance(), predicate);
77 return find(key);
78 }
79
80 @Override
81 public CdmPreference findTaxEditor(IPreferencePredicate<?> predicate){
82 PrefKey key = CdmPreference.NewKey(PreferenceSubject.NewTaxEditorInstance(), predicate);
83 return find(key);
84 }
85
86 @Override
87 @Transactional(readOnly = false)
88 public void set(CdmPreference preference) {
89 dao.set(preference);
90 cachePut(preference);
91 }
92
93 @Override
94 @Transactional(readOnly = false)
95 public void remove(PrefKey key) {
96 dao.remove(key);
97 removeFromCache(key);
98 }
99
100 @Override
101 public long count() {
102 return dao.count();
103 }
104
105 @Override
106 public List<CdmPreference> list() {
107 if(!cacheIsComplete) {
108 cacheFullUpdate();
109 }
110 return new ArrayList<>(cacheValues());
111 }
112
113 @Override
114 public List<CdmPreference> list(IPreferencePredicate<?> predicate) {
115 // using the cache for this method makes not much sense
116 return dao.list(predicate);
117 }
118
119 @Override
120 public CdmPreference find(TaxonNode taxonNode, String predicate) {
121
122 return dao.find(taxonNode, predicate);
123 // caching of taxon node related prefs disabled, see https://dev.e-taxonomy.eu/redmine/issues/9152#note-8
124 // code has been left here in case we decide later for solution 2 which has been suggested also in comment 5
125 // String cacheKey = cacheKey(taxonNode, predicate);
126 // return fromCacheOrFind(taxonNode, predicate, cacheKey);
127 }
128
129 @Override
130 public CdmPreference find(TaxonNode taxonNode, IPreferencePredicate<?> predicate){
131 return find(taxonNode, predicate.getKey());
132 }
133
134 // ********************** NOT YET HANDLED *******************/
135
136 @Override
137 public List<CdmPreference> list(String subject, String predicate) {
138 //FIXME
139 throw new RuntimeException("list(String, String) not yet implemented" );
140 }
141
142 @Override
143 //this method is only partly implemented
144 public CdmPreference find(CdmBase taxonNodeRelatedCdmBase, String predicate) {
145 TaxonNode taxonNode = mapToTaxonNode(taxonNodeRelatedCdmBase);
146 return dao.find(taxonNode, predicate);
147 }
148
149 private TaxonNode mapToTaxonNode(CdmBase taxonNodeRelatedCdmBase) {
150 if (taxonNodeRelatedCdmBase == null){
151 return null;
152 }else if (taxonNodeRelatedCdmBase.isInstanceOf(TaxonNode.class)){
153 return CdmBase.deproxy(taxonNodeRelatedCdmBase, TaxonNode.class);
154 }else{
155 throw new RuntimeException("mapToTaxonNode not yet implemented for " + taxonNodeRelatedCdmBase.getClass().getSimpleName());
156 }
157 }
158
159 // ====================== Cache methods ======================= //
160
161 /**
162 * Concatenates subject and predicate as key for the cache map
163 */
164 private String cacheKey(PrefKey key) {
165 return key.getSubject() + "@" + key.getPredicate();
166 }
167
168 /**
169 * @deprecated caching of taxon node related prefs disabled, see https://dev.e-taxonomy.eu/redmine/issues/9152#note-8
170 * code has been left here in case we decide later for solution 2 which has been suggested also in comment 5
171 */
172 @Deprecated
173 private String cacheKey(TaxonNode taxonNode, String predicate) {
174 return taxonNode.treeIndex() + predicate;
175 }
176
177
178 // --------------- non locking cache read methods --------------- //
179
180 protected Collection<CdmPreference> cacheValues() {
181 waitForCache();
182 return cache.values();
183 }
184
185 protected CdmPreference fromCacheGet(PrefKey key, String cacheKey) {
186 waitForCache();
187 return cache.computeIfAbsent(cacheKey, k -> dao.get(key));
188 }
189
190
191 /**
192 * @deprecated caching of taxon node related prefs disabled, see https://dev.e-taxonomy.eu/redmine/issues/9152#note-8
193 * code has been left here in case we decide later for solution 2 which has been suggested also in comment 5
194 */
195 @Deprecated
196 protected CdmPreference fromCacheOrFind(TaxonNode taxonNode, String predicate, String cacheKey) {
197 waitForCache();
198 return cache.computeIfAbsent(cacheKey, k -> dao.find(taxonNode, predicate));
199 }
200
201 // --------------- cache locking methods --------------- //
202
203 protected void cachePut(CdmPreference preference) {
204 waitForCache();
205 cacheIsLocked = true;
206 cache.put(cacheKey(preference.getKey()), preference);
207 cacheIsLocked = false;
208 }
209
210
211 protected void removeFromCache(PrefKey key) {
212 waitForCache();
213 cacheIsLocked = true;
214 cache.remove(cacheKey(key));
215 cacheIsLocked = false;
216 }
217
218 protected void cacheFullUpdate() {
219 waitForCache();
220 cacheIsLocked = true;
221 cache.clear();
222 for(CdmPreference pref : dao.list()){
223 cache.put(cacheKey(pref.getKey()), pref);
224 }
225 cacheIsComplete = true;
226 cacheIsLocked = false;
227 }
228
229 protected void waitForCache() {
230 while(cacheIsLocked) {
231 try {
232 Thread.sleep(1);
233 } catch (InterruptedException e) {
234 // just keep on sleeping, we may improve this later on
235 }
236 }
237 }
238
239 }