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