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