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.Counters;
33 import co.stateful.spi.Locks;
34 import co.stateful.spi.User;
35 import com.google.common.base.Joiner;
36 import com.google.common.base.Splitter;
37 import com.google.common.collect.Iterables;
38 import com.jcabi.aspects.Cacheable;
39 import com.jcabi.aspects.Immutable;
40 import com.jcabi.aspects.Loggable;
41 import com.jcabi.aspects.Tv;
42 import com.jcabi.dynamo.Attributes;
43 import com.jcabi.dynamo.Conditions;
44 import com.jcabi.dynamo.Credentials;
45 import com.jcabi.dynamo.Item;
46 import com.jcabi.dynamo.QueryValve;
47 import com.jcabi.dynamo.Region;
48 import com.jcabi.manifests.Manifests;
49 import com.jcabi.urn.URN;
50 import java.io.IOException;
51 import java.util.Iterator;
52 import java.util.Locale;
53 import java.util.concurrent.TimeUnit;
54 import lombok.EqualsAndHashCode;
55 import lombok.ToString;
56 import org.apache.commons.codec.digest.DigestUtils;
57 import org.apache.commons.lang3.RandomStringUtils;
58
59
60
61
62
63
64
65
66 @Immutable
67 @ToString
68 @EqualsAndHashCode(of = "name")
69 @Loggable(Loggable.DEBUG)
70 final class DefaultUser implements User {
71
72
73
74
75 public static final String TOKENS = "tokens";
76
77
78
79
80 public static final String HASH = "urn";
81
82
83
84
85 public static final String ATTR_TOKEN = "token";
86
87
88
89
90 private final transient URN name;
91
92
93
94
95 private final transient Region region;
96
97
98
99
100
101 DefaultUser(final URN urn) {
102 this.name = urn;
103 final String key = Manifests.read("Stateful-DynamoKey");
104 Credentials creds = new Credentials.Simple(
105 key,
106 Manifests.read("Stateful-DynamoSecret")
107 );
108 if ("AAAAABBBBBAAAAABBBBB".equals(key)) {
109 creds = new Credentials.Direct(
110 creds, Integer.parseInt(System.getProperty("dynamo.port"))
111 );
112 }
113 this.region = new Region.Prefixed(
114 new Region.Simple(creds),
115 Manifests.read("Stateful-DynamoPrefix")
116 );
117 }
118
119 @Override
120 @Cacheable(forever = true)
121 public boolean exists() {
122 return this.region.table(DefaultUser.TOKENS)
123 .frame().through(new QueryValve())
124 .where(DefaultUser.HASH, Conditions.equalTo(this.name))
125 .iterator().hasNext();
126 }
127
128 @Override
129 @Cacheable(lifetime = 1, unit = TimeUnit.HOURS)
130 public String token() throws IOException {
131 final Iterator<Item> items = this.region.table(DefaultUser.TOKENS)
132 .frame().through(new QueryValve())
133 .where(DefaultUser.HASH, Conditions.equalTo(this.name))
134 .iterator();
135 final String token;
136 if (items.hasNext()) {
137 token = items.next().get(DefaultUser.ATTR_TOKEN).getS();
138 } else {
139 this.refresh();
140 token = this.token();
141 }
142 return token;
143 }
144
145 @Override
146 @Cacheable.FlushAfter
147 public void refresh() throws IOException {
148 this.region.table(DefaultUser.TOKENS).put(
149 new Attributes()
150 .with(DefaultUser.HASH, this.name)
151 .with(
152 DefaultUser.ATTR_TOKEN,
153 Joiner.on('-').join(
154 Iterables.limit(
155 Splitter.fixedLength(Tv.FOUR).split(
156 DigestUtils.md5Hex(
157 RandomStringUtils.random(Tv.TEN)
158 ).toUpperCase(Locale.ENGLISH)
159 ),
160 Tv.FOUR
161 )
162 )
163 )
164 );
165 }
166
167 @Override
168 @Cacheable(lifetime = 1, unit = TimeUnit.HOURS)
169 public Counters counters() {
170 return new DyCounters(this.region.table(DyCounters.TBL), this.name);
171 }
172
173 @Override
174 public Locks locks() {
175 return new DyLocks(this.region.table(DyLocks.TBL), this.name);
176 }
177 }