1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package co.stateful.core;
31
32 import co.stateful.spi.Locks;
33 import com.amazonaws.AmazonClientException;
34 import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
35 import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException;
36 import com.amazonaws.services.dynamodbv2.model.ExpectedAttributeValue;
37 import com.amazonaws.services.dynamodbv2.model.PutItemRequest;
38 import com.google.common.base.Predicate;
39 import com.google.common.base.Predicates;
40 import com.google.common.collect.ImmutableMap;
41 import com.google.common.collect.Iterables;
42 import com.jcabi.aspects.Immutable;
43 import com.jcabi.aspects.Loggable;
44 import com.jcabi.dynamo.Attributes;
45 import com.jcabi.dynamo.Conditions;
46 import com.jcabi.dynamo.Item;
47 import com.jcabi.dynamo.QueryValve;
48 import com.jcabi.dynamo.Table;
49 import com.jcabi.urn.URN;
50 import java.io.IOException;
51 import java.util.Iterator;
52 import java.util.Map;
53 import lombok.EqualsAndHashCode;
54 import lombok.ToString;
55
56
57
58
59
60
61
62
63 @Immutable
64 @ToString
65 @EqualsAndHashCode(of = "owner")
66 @Loggable(Loggable.DEBUG)
67 final class DyLocks implements Locks {
68
69
70
71
72 public static final String TBL = "locks";
73
74
75
76
77 public static final String HASH = "urn";
78
79
80
81
82 public static final String RANGE = "name";
83
84
85
86
87 public static final String ATTR_LABEL = "label";
88
89
90
91
92 private final transient Table table;
93
94
95
96
97 private final transient URN owner;
98
99
100
101
102
103
104 DyLocks(final Table tbl, final URN urn) {
105 this.table = tbl;
106 this.owner = urn;
107 }
108
109 @Override
110 public Map<String, String> names() {
111 final ImmutableMap.Builder<String, String> map =
112 new ImmutableMap.Builder<String, String>();
113 Iterables.all(
114 this.table.frame().through(
115 new QueryValve().withAttributesToGet(
116 DyLocks.ATTR_LABEL
117 )
118 ).where(DyLocks.HASH, Conditions.equalTo(this.owner)),
119 new Predicate<Item>() {
120 @Override
121 public boolean apply(final Item item) {
122 try {
123 map.put(
124 item.get(DyLocks.RANGE).getS(),
125 item.get(DyLocks.ATTR_LABEL).getS()
126 );
127 } catch (final IOException ex) {
128 throw new IllegalStateException(ex);
129 }
130 return true;
131 }
132 }
133 );
134 return map.build();
135 }
136
137 @Override
138 public String lock(final String name, final String label)
139 throws IOException {
140 final AmazonDynamoDB aws = this.table.region().aws();
141 String msg = "";
142 try {
143 final PutItemRequest request = new PutItemRequest();
144 request.setTableName(this.table.name());
145 request.setItem(
146 new Attributes()
147 .with(DyLocks.HASH, this.owner)
148 .with(DyLocks.RANGE, name)
149 .with(DyLocks.ATTR_LABEL, label)
150 );
151 request.setExpected(
152 new ImmutableMap.Builder<String, ExpectedAttributeValue>().put(
153 DyLocks.ATTR_LABEL,
154 new ExpectedAttributeValue().withExists(false)
155 ).build()
156 );
157 aws.putItem(request);
158 } catch (final ConditionalCheckFailedException ex) {
159 msg = ex.getLocalizedMessage();
160 } catch (final AmazonClientException ex) {
161 throw new IOException(ex);
162 } finally {
163 aws.shutdown();
164 }
165 return msg;
166 }
167
168 @Override
169 public void unlock(final String name) {
170 Iterables.removeIf(
171 this.table.frame()
172 .through(new QueryValve())
173 .where(DyLocks.HASH, this.owner.toString())
174 .where(DyLocks.RANGE, name),
175 Predicates.alwaysTrue()
176 );
177 }
178
179 @Override
180 public String unlock(final String name, final String label)
181 throws IOException {
182 final Iterator<Item> items = this.table.frame()
183 .through(new QueryValve())
184 .where(DyLocks.HASH, this.owner.toString())
185 .where(DyLocks.RANGE, name)
186 .iterator();
187 String msg = "";
188 if (items.hasNext()) {
189 final String required = items.next().get(DyLocks.ATTR_LABEL).getS();
190 if (required.equals(label)) {
191 this.unlock(name);
192 } else {
193 msg = required;
194 }
195 }
196 return msg;
197 }
198 }