1 package com.atlassian.plugin.manager.store;
2
3 import com.atlassian.annotations.Internal;
4 import com.atlassian.plugin.manager.PluginPersistentState;
5 import com.atlassian.plugin.manager.PluginPersistentStateStore;
6 import io.atlassian.util.concurrent.ManagedLock;
7 import io.atlassian.util.concurrent.ManagedLocks;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10
11 import java.util.concurrent.TimeUnit;
12 import java.util.concurrent.atomic.AtomicInteger;
13 import java.util.concurrent.locks.Condition;
14 import java.util.concurrent.locks.Lock;
15 import java.util.concurrent.locks.ReadWriteLock;
16 import java.util.concurrent.locks.ReentrantLock;
17 import java.util.concurrent.locks.ReentrantReadWriteLock;
18 import java.util.function.Supplier;
19
20 import static com.atlassian.plugin.util.EnumUtils.enumValueFromProperty;
21 import static com.google.common.base.Preconditions.checkNotNull;
22
23
24
25
26
27
28
29
30
31 public class SynchronizedPluginPersistentStateStore implements PluginPersistentStateStore {
32 private static final Logger log = LoggerFactory.getLogger(SynchronizedPluginPersistentStateStore.class);
33
34 @Internal
35 public enum LockMode {
36 UNLOCKED {
37 @Override
38 ReadWriteLock getReadWriteLock() {
39 return new CommonReadWriteLock(new NoOpLock());
40 }
41 },
42 SIMPLE {
43 @Override
44 ReadWriteLock getReadWriteLock() {
45 return new CommonReadWriteLock(new ReentrantLock());
46 }
47
48 },
49 FAIRSIMPLE {
50 @Override
51 ReadWriteLock getReadWriteLock() {
52 return new CommonReadWriteLock(new ReentrantLock(true));
53 }
54 },
55 READWRITE {
56 @Override
57 ReadWriteLock getReadWriteLock() {
58 return new ReentrantReadWriteLock();
59 }
60 },
61 FAIRREADWRITE {
62 @Override
63 ReadWriteLock getReadWriteLock() {
64 return new ReentrantReadWriteLock(true);
65 }
66 };
67
68 private static final String PROPERTY_NAME = SynchronizedPluginPersistentStateStore.class.getName() + ".lockMode";
69
70 static LockMode current() {
71 return enumValueFromProperty(PROPERTY_NAME, LockMode.values(), LockMode.READWRITE);
72 }
73
74 abstract ReadWriteLock getReadWriteLock();
75
76 }
77
78 @Internal
79 public static String getLockModeProperty() {
80 return LockMode.PROPERTY_NAME;
81 }
82
83 private final PluginPersistentStateStore delegate;
84 private final ManagedLock.ReadWrite lock;
85 private final AtomicInteger loadConcurrency = new AtomicInteger(0);
86 private final AtomicInteger saveConcurrency = new AtomicInteger(0);
87
88 public SynchronizedPluginPersistentStateStore(final PluginPersistentStateStore delegate) {
89 this(delegate, LockMode.current().getReadWriteLock());
90 }
91
92 public SynchronizedPluginPersistentStateStore(final PluginPersistentStateStore delegate, final ReadWriteLock lock) {
93 this.delegate = checkNotNull(delegate);
94 this.lock = ManagedLocks.manageReadWrite(checkNotNull(lock));
95 }
96
97 @Override
98 public void save(final PluginPersistentState state) {
99 final int writes = saveConcurrency.incrementAndGet();
100 log.debug("save concurrency {}", writes);
101 lock.write().withLock(() -> delegate.save(state));
102 saveConcurrency.decrementAndGet();
103 }
104
105 @Override
106 public PluginPersistentState load() {
107 final int reads = loadConcurrency.incrementAndGet();
108 log.debug("load concurrency {}", reads);
109 final PluginPersistentState pluginPersistentState = lock.read().withLock((Supplier<PluginPersistentState>) delegate::load);
110 loadConcurrency.decrementAndGet();
111 return pluginPersistentState;
112 }
113
114 static class NoOpLock implements Lock {
115 @Override
116 public void lock() {
117 }
118
119 @Override
120 public void lockInterruptibly() {
121 }
122
123 @Override
124 public boolean tryLock() {
125 return true;
126 }
127
128 @Override
129 public boolean tryLock(final long time, final TimeUnit unit) {
130 return true;
131 }
132
133 @Override
134 public void unlock() {
135 }
136
137 @Override
138 public Condition newCondition() {
139 throw new UnsupportedOperationException("Not implemented");
140 }
141 }
142
143 static class CommonReadWriteLock implements ReadWriteLock {
144 private final Lock lock;
145
146 public CommonReadWriteLock(final Lock lock) {
147 this.lock = lock;
148 }
149
150 @Override
151 public Lock readLock() {
152 return lock;
153 }
154
155 @Override
156 public Lock writeLock() {
157 return lock;
158 }
159 }
160 }