001
014
015 package com.liferay.portlet;
016
017 import com.liferay.portal.kernel.exception.PortalException;
018 import com.liferay.portal.kernel.exception.SystemException;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.transaction.Propagation;
022 import com.liferay.portal.kernel.transaction.TransactionAttribute;
023 import com.liferay.portal.kernel.transaction.TransactionInvokerUtil;
024 import com.liferay.portal.kernel.util.HashUtil;
025 import com.liferay.portal.kernel.util.SetUtil;
026 import com.liferay.portal.kernel.util.StringPool;
027 import com.liferay.portal.kernel.util.Validator;
028 import com.liferay.portal.service.PortalPreferencesLocalServiceUtil;
029 import com.liferay.portal.service.persistence.PortalPreferencesUtil;
030
031 import java.io.IOException;
032 import java.io.Serializable;
033
034 import java.util.Arrays;
035 import java.util.Collections;
036 import java.util.ConcurrentModificationException;
037 import java.util.HashMap;
038 import java.util.Map;
039 import java.util.Set;
040 import java.util.concurrent.Callable;
041
042 import javax.portlet.ReadOnlyException;
043
044 import org.hibernate.StaleObjectStateException;
045
046
050 public class PortalPreferencesImpl
051 extends BasePreferencesImpl
052 implements Cloneable, PortalPreferences, Serializable {
053
054 public static final TransactionAttribute SUPPORTS_TRANSACTION_ATTRIBUTE;
055
056 static {
057 TransactionAttribute.Builder builder =
058 new TransactionAttribute.Builder();
059
060 builder.setPropagation(Propagation.SUPPORTS);
061 builder.setReadOnly(true);
062 builder.setRollbackForClasses(
063 PortalException.class, SystemException.class);
064
065 SUPPORTS_TRANSACTION_ATTRIBUTE = builder.build();
066 }
067
068 public PortalPreferencesImpl() {
069 this(0, 0, null, Collections.<String, Preference>emptyMap(), false);
070 }
071
072 public PortalPreferencesImpl(
073 com.liferay.portal.model.PortalPreferences portalPreferences,
074 boolean signedIn) {
075
076 this(
077 portalPreferences.getOwnerId(), portalPreferences.getOwnerType(),
078 portalPreferences.getPreferences(),
079 PortletPreferencesFactoryImpl.createPreferencesMap(
080 portalPreferences.getPreferences()),
081 signedIn);
082
083 _portalPreferences =
084 (com.liferay.portal.model.PortalPreferences)
085 portalPreferences.clone();
086 }
087
088 public PortalPreferencesImpl(
089 long ownerId, int ownerType, String xml,
090 Map<String, Preference> preferences, boolean signedIn) {
091
092 super(ownerId, ownerType, xml, preferences);
093
094 _signedIn = signedIn;
095 }
096
097 @Override
098 public PortalPreferencesImpl clone() {
099 if (_portalPreferences == null) {
100 return new PortalPreferencesImpl(
101 getOwnerId(), getOwnerType(), getOriginalXML(),
102 new HashMap<>(getOriginalPreferences()), isSignedIn());
103 }
104
105 return new PortalPreferencesImpl(_portalPreferences, isSignedIn());
106 }
107
108 @Override
109 public boolean equals(Object obj) {
110 if (this == obj) {
111 return true;
112 }
113
114 if (!(obj instanceof PortalPreferencesImpl)) {
115 return false;
116 }
117
118 PortalPreferencesImpl portalPreferences = (PortalPreferencesImpl)obj;
119
120 if ((getOwnerId() == portalPreferences.getOwnerId()) &&
121 (getOwnerType() == portalPreferences.getOwnerType()) &&
122 getPreferences().equals(portalPreferences.getPreferences())) {
123
124 return true;
125 }
126 else {
127 return false;
128 }
129 }
130
131 @Override
132 public long getUserId() {
133 return _userId;
134 }
135
136 @Override
137 public String getValue(String namespace, String key) {
138 return getValue(namespace, key, null);
139 }
140
141 @Override
142 public String getValue(String namespace, String key, String defaultValue) {
143 key = _encodeKey(namespace, key);
144
145 return super.getValue(key, defaultValue);
146 }
147
148 @Override
149 public String[] getValues(String namespace, String key) {
150 return getValues(namespace, key, null);
151 }
152
153 @Override
154 public String[] getValues(
155 String namespace, String key, String[] defaultValue) {
156
157 key = _encodeKey(namespace, key);
158
159 return super.getValues(key, defaultValue);
160 }
161
162 @Override
163 public int hashCode() {
164 int hashCode = HashUtil.hash(0, getOwnerId());
165
166 hashCode = HashUtil.hash(hashCode, getOwnerType());
167 hashCode = HashUtil.hash(hashCode, getPreferences());
168
169 return hashCode;
170 }
171
172 @Override
173 public boolean isSignedIn() {
174 return _signedIn;
175 }
176
177 @Override
178 public void reset(final String key) throws ReadOnlyException {
179 if (isReadOnly(key)) {
180 throw new ReadOnlyException(key);
181 }
182
183 String[] values = super.getValues(key, null);
184
185 if (values == null) {
186 return;
187 }
188
189 Callable<Void> callable = new Callable<Void>() {
190
191 @Override
192 public Void call() {
193 Map<String, Preference> modifiedPreferences =
194 getModifiedPreferences();
195
196 modifiedPreferences.remove(key);
197
198 return null;
199 }
200
201 };
202
203 try {
204 retryableStore(callable, key);
205 }
206 catch (ConcurrentModificationException cme) {
207 throw cme;
208 }
209 catch (Throwable t) {
210 _log.error(t, t);
211 }
212 }
213
214 @Override
215 public void resetValues(String namespace) {
216 Map<String, Preference> preferences = getPreferences();
217
218 try {
219 for (Map.Entry<String, Preference> entry : preferences.entrySet()) {
220 String key = entry.getKey();
221
222 if (key.startsWith(namespace) && !isReadOnly(key)) {
223 reset(key);
224 }
225 }
226 }
227 catch (ConcurrentModificationException cme) {
228 throw cme;
229 }
230 catch (Throwable t) {
231 _log.error(t, t);
232 }
233 }
234
235 @Override
236 public void setSignedIn(boolean signedIn) {
237 _signedIn = signedIn;
238 }
239
240 @Override
241 public void setUserId(long userId) {
242 _userId = userId;
243 }
244
245 @Override
246 public void setValue(String namespace, String key, final String value) {
247 if (Validator.isNull(key) || key.equals(_RANDOM_KEY)) {
248 return;
249 }
250
251 final String encodedKey = _encodeKey(namespace, key);
252
253 try {
254 if (value == null) {
255 reset(encodedKey);
256
257 return;
258 }
259
260 String[] oldValues = super.getValues(encodedKey, null);
261
262 if ((oldValues != null) && (oldValues.length == 1) &&
263 value.equals(oldValues[0])) {
264
265 return;
266 }
267
268 Callable<Void> callable = new Callable<Void>() {
269
270 @Override
271 public Void call() throws ReadOnlyException {
272 PortalPreferencesImpl.super.setValue(encodedKey, value);
273
274 return null;
275 }
276
277 };
278
279 if (_signedIn) {
280 retryableStore(callable, encodedKey);
281 }
282 else {
283 callable.call();
284 }
285 }
286 catch (ConcurrentModificationException cme) {
287 throw cme;
288 }
289 catch (Throwable t) {
290 _log.error(t, t);
291 }
292 }
293
294 @Override
295 public void setValues(String namespace, String key, final String[] values) {
296 if (Validator.isNull(key) || key.equals(_RANDOM_KEY)) {
297 return;
298 }
299
300 final String encodedKey = _encodeKey(namespace, key);
301
302 try {
303 if (values == null) {
304 reset(encodedKey);
305
306 return;
307 }
308
309 if (values.length == 1) {
310 setValue(namespace, key, values[0]);
311
312 return;
313 }
314
315 String[] oldValues = super.getValues(encodedKey, null);
316
317 if (oldValues != null) {
318 Set<String> valuesSet = SetUtil.fromArray(values);
319 Set<String> oldValuesSet = SetUtil.fromArray(oldValues);
320
321 if (valuesSet.equals(oldValuesSet)) {
322 return;
323 }
324 }
325
326 Callable<Void> callable = new Callable<Void>() {
327
328 @Override
329 public Void call() throws ReadOnlyException {
330 PortalPreferencesImpl.super.setValues(encodedKey, values);
331
332 return null;
333 }
334
335 };
336
337 if (_signedIn) {
338 retryableStore(callable, encodedKey);
339 }
340 else {
341 callable.call();
342 }
343 }
344 catch (ConcurrentModificationException cme) {
345 throw cme;
346 }
347 catch (Throwable t) {
348 _log.error(t, t);
349 }
350 }
351
352 @Override
353 public void store() throws IOException {
354 try {
355 if (_portalPreferences == null) {
356 _portalPreferences =
357 PortalPreferencesLocalServiceUtil.updatePreferences(
358 getOwnerId(), getOwnerType(), this);
359 }
360 else {
361 PortalPreferencesWrapperCacheUtil.remove(
362 getOwnerId(), getOwnerType());
363
364 _portalPreferences.setPreferences(toXML());
365
366 PortalPreferencesLocalServiceUtil.updatePortalPreferences(
367 _portalPreferences);
368
369 _portalPreferences = _reload(getOwnerId(), getOwnerType());
370 }
371 }
372 catch (Throwable t) {
373 throw new IOException(t);
374 }
375 }
376
377 protected boolean isCausedByStaleObjectException(Throwable t) {
378 Throwable cause = t.getCause();
379
380 while (t != cause) {
381 if (t instanceof StaleObjectStateException) {
382 return true;
383 }
384
385 if (cause == null) {
386 return false;
387 }
388
389 t = cause;
390
391 cause = t.getCause();
392 }
393
394 return false;
395 }
396
397 protected void retryableStore(Callable<?> callable, String key)
398 throws Throwable {
399
400 String[] originalValues = super.getValues(key, null);
401
402 while (true) {
403 try {
404 callable.call();
405
406 store();
407
408 return;
409 }
410 catch (Exception e) {
411 if (isCausedByStaleObjectException(e)) {
412 long ownerId = getOwnerId();
413 int ownerType = getOwnerType();
414
415 com.liferay.portal.model.PortalPreferences
416 portalPreferences = _reload(ownerId, ownerType);
417
418 if (portalPreferences == null) {
419 continue;
420 }
421
422 PortalPreferencesImpl portalPreferencesImpl =
423 new PortalPreferencesImpl(
424 portalPreferences, isSignedIn());
425
426 if (!Arrays.equals(
427 originalValues,
428 portalPreferencesImpl.getValues(
429 key, (String[])null))) {
430
431 throw new ConcurrentModificationException();
432 }
433
434 reset();
435
436 setOriginalPreferences(
437 portalPreferencesImpl.getOriginalPreferences());
438
439 setOriginalXML(portalPreferences.getPreferences());
440
441 _portalPreferences = portalPreferences;
442 }
443 else {
444 throw e;
445 }
446 }
447 }
448 }
449
450 private String _encodeKey(String namespace, String key) {
451 if (Validator.isNull(namespace)) {
452 return key;
453 }
454 else {
455 return namespace.concat(StringPool.POUND).concat(key);
456 }
457 }
458
459 private com.liferay.portal.model.PortalPreferences _reload(
460 final long ownerId, final int ownerType)
461 throws Throwable {
462
463 return TransactionInvokerUtil.invoke(
464 SUPPORTS_TRANSACTION_ATTRIBUTE,
465 new Callable<com.liferay.portal.model.PortalPreferences>() {
466
467 @Override
468 public com.liferay.portal.model.PortalPreferences call() {
469 return PortalPreferencesUtil.fetchByO_O(
470 ownerId, ownerType, false);
471 }
472
473 });
474 }
475
476 private static final String _RANDOM_KEY = "r";
477
478 private static final Log _log = LogFactoryUtil.getLog(
479 PortalPreferencesImpl.class);
480
481 private com.liferay.portal.model.PortalPreferences _portalPreferences;
482 private boolean _signedIn;
483 private long _userId;
484
485 }