generated from C4T-BuT-S4D/ad-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathVaccineExchangeService.java
219 lines (172 loc) · 7.62 KB
/
VaccineExchangeService.java
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package exchange;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.Striped;
import io.grpc.Status;
import io.grpc.StatusException;
import exchange.proto.Exchange.Vaccine;
import exchange.proto.Exchange.SellInfo;
import exchange.proto.Exchange.VaccineInfo;
import exchange.proto.Exchange.CreateVaccineRequest;
import exchange.proto.Exchange.ListVaccineInfo;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
public class VaccineExchangeService {
private final VaccineExchangeStorage storage;
private static double defaultBalance = 5.0;
private Striped<Lock> locks;
private static long expireSeconds = 60 * 10;
private static long fetchLastN = 250;
public VaccineExchangeService(VaccineExchangeStorage store) {
storage = store;
locks = Striped.lock(4096);
}
String loginUser(String userId, String password) throws StatusException {
try (VaccineExchangeStorage.Context ctx = storage.newContext()) {
String passwordDb = storage.findUserPassword(ctx, userId);
if (Strings.isNullOrEmpty(passwordDb)) {
throw new StatusException(Status.NOT_FOUND.withDescription("user not found"));
}
if (!passwordDb.equals(password)) {
throw new StatusException(Status.FAILED_PRECONDITION.withDescription("invalid password provided"));
}
String token = RandomHelper.getString(30);
storage.saveToken(ctx, userId, token, expireSeconds);
return token;
}
}
void createUser(String userId, String password) throws Exception {
try (VaccineExchangeStorage.Context ctx = storage.newContext()) {
storage.createUser(ctx, userId, password);
storage.setBalance(ctx, userId, defaultBalance);
}
}
String getUserId(String token) throws StatusException {
if (Strings.isNullOrEmpty(token)) {
throw new StatusException(Status.UNAUTHENTICATED.withDescription("no auth token provided"));
}
String userId = storage.getUserIdFromToken(token);
if (Strings.isNullOrEmpty(userId)) {
throw new StatusException(Status.UNAUTHENTICATED.withDescription("invalid auth token provided"));
}
return userId;
}
private void validatePrice(double price) throws Exception {
if (price <= 0) {
throw new Exception("price should be positive");
}
}
private SellInfo createSellInfo(VaccineExchangeStorage.Context ctx, double price, VaccineInfo info) {
String id = UUID.randomUUID().toString();
storage.saveSell(ctx, id, info, price);
return SellInfo.newBuilder().setId(id).setPrice(price).build();
}
Vaccine createVaccine(String userId, CreateVaccineRequest request) throws Exception {
if (Strings.isNullOrEmpty(request.getRnaInfo())) {
throw new Exception("rna info empty");
}
if (Strings.isNullOrEmpty(request.getName())) {
throw new Exception("vaccine name empty");
}
validatePrice(request.getPrivatePrice());
if (request.hasPublicPrice()) {
validatePrice(request.getPublicPrice().getPrice());
}
try (VaccineExchangeStorage.Context ctx = storage.newContext()) {
if (storage.getUserPrivateStock(ctx, userId) != null) {
throw new StatusException(Status.ALREADY_EXISTS.withDescription("vaccine already exists for this user"));
}
VaccineInfo info = VaccineInfo
.newBuilder()
.setRnaInfo(request.getRnaInfo())
.setName(request.getName())
.setSellerId(userId)
.build();
Vaccine.Builder builder = Vaccine.newBuilder().setInfo(info);
SellInfo privateInfo = createSellInfo(ctx, request.getPrivatePrice(), info);
builder.setPrivate(privateInfo);
storage.saveUserPrivateStock(ctx, userId, privateInfo.getId());
if (!request.hasPublicPrice()) {
return builder.build();
}
SellInfo publicInfo = createSellInfo(ctx, request.getPublicPrice().getPrice(), info);
storage.saveUserPublicStock(ctx, userId, publicInfo.getId());
storage.addToPublicList(ctx,
ListVaccineInfo
.newBuilder()
.setName(request.getName())
.setStockId(publicInfo.getId())
.build()
);
return builder.setPublic(publicInfo).build();
}
}
VaccineInfo buy(String userId, String stockId) throws Exception {
Lock sellLock = locks.get(stockId);
sellLock.lock();
try (VaccineExchangeStorage.Context ctx = storage.newContext()) {
VaccineInfo info = storage.getVaccineInfo(ctx, stockId);
String sellerId = info.getSellerId();
if (userId.equals(sellerId)) {
throw new Exception("you can't buy your own vaccine");
}
String minUserId = sellerId;
String maxUserId = userId;
if (minUserId.compareTo(maxUserId) > 0) {
String tmp = minUserId;
minUserId = maxUserId;
maxUserId = tmp;
}
Lock minLock = locks.get(minUserId);
Lock maxLock = locks.get(maxUserId);
minLock.lock();
maxLock.lock();
try {
double userBalance = storage.getBalance(ctx, userId);
double price = storage.getPrice(ctx, stockId);
if (userBalance < price) {
throw new Exception("not enough money to buy");
}
userBalance -= price;
storage.setBalance(ctx, userId, userBalance);
storage.setBalance(ctx, sellerId, storage.getBalance(ctx, sellerId) + price);
storage.savePrice(ctx, stockId, price * 2);
return info;
} finally {
maxLock.unlock();
minLock.unlock();
}
} finally {
sellLock.unlock();
}
}
double getBalance(String userId) {
return storage.getBalance(userId);
}
double getPrice(String stockId) throws Exception {
return storage.getPrice(stockId);
}
Vaccine getUserVaccine(String userId) throws Exception {
try (VaccineExchangeStorage.Context ctx = storage.newContext()) {
String privateStockId = storage.getUserPrivateStock(ctx, userId);
if (Strings.isNullOrEmpty(privateStockId)) {
throw new StatusException(Status.NOT_FOUND.withDescription("sell info not found for user"));
}
VaccineInfo info = storage.getVaccineInfo(ctx, privateStockId);
double privatePrice = storage.getPrice(ctx, privateStockId);
Vaccine.Builder builder = Vaccine
.newBuilder()
.setInfo(info)
.setPrivate(SellInfo.newBuilder().setPrice(privatePrice).setId(privateStockId));
String publicStockId = storage.getUserPublicStock(userId);
if (publicStockId == null) {
return builder.build();
}
double publicPrice = storage.getPrice(publicStockId);
return builder.setPublic(SellInfo.newBuilder().setId(publicStockId).setPrice(publicPrice)).build();
}
}
List<ListVaccineInfo> getLatestInfos() {
return storage.getPublicInfos(fetchLastN);
}
}