1 package net.avcompris.examples.users3.dao.impl;
2
3 import static com.google.common.base.Preconditions.checkNotNull;
4 import static net.avcompris.commons.query.impl.FilteringsFactory.match;
5 import static net.avcompris.commons3.databeans.DataBeans.instantiate;
6
7 import java.io.IOException;
8 import java.sql.SQLException;
9 import java.util.ArrayList;
10 import java.util.Comparator;
11 import java.util.ConcurrentModificationException;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.concurrent.ConcurrentHashMap;
15
16 import javax.annotation.Nullable;
17
18 import org.joda.time.DateTime;
19 import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.stereotype.Component;
21
22 import net.avcompris.commons3.dao.exception.DuplicateEntityException;
23 import net.avcompris.commons3.dao.impl.AbstractDao;
24 import net.avcompris.commons3.utils.Clock;
25 import net.avcompris.examples.users3.dao.UserDto;
26 import net.avcompris.examples.users3.dao.UsersDao;
27 import net.avcompris.examples.users3.dao.UsersDto;
28 import net.avcompris.examples.users3.dao.UsersDtoQuery;
29 import net.avcompris.examples.users3.dao.UsersDtoQuery.SortBy;
30 import net.avcompris.examples.users3.dao.impl.MutableUserDto;
31 import net.avcompris.examples.users3.dao.impl.MutableUsersDto;
32 import net.avcompris.examples.users3.query.UserFiltering;
33
34 @Component
35 public final class UsersDaoInMemory extends AbstractDao implements UsersDao {
36
37 private final Map<String, MutableUserDto> users = new ConcurrentHashMap<>();
38
39 @Autowired
40 public UsersDaoInMemory(final Clock clock) {
41
42 super(clock);
43 }
44
45 @Override
46 public UsersDto getUsers(final UsersDtoQuery query) throws SQLException, IOException {
47
48 checkNotNull(query, "query");
49
50 final UserFiltering filtering = query.getFiltering();
51 final SortBy[] sortBys = query.getSortBys();
52 final int start = query.getStart();
53 final int limit = query.getLimit();
54
55 final List<UserDto> extract = new ArrayList<>();
56
57 for (final MutableUserDto user : users.values()) {
58
59 if (filtering == null || match(user, filtering)) {
60
61 extract.add(user);
62 }
63 }
64
65 final MutableUsersDto users = instantiate(MutableUsersDto.class)
66 .setTotal(extract.size());
67
68 final Comparator<? super UserDto> comparator = Comparators.get(sortBys);
69
70 extract.sort(comparator);
71
72 int offset = 0;
73
74 for (final UserDto user : extract) {
75
76 if (offset >= start + limit) {
77
78 break;
79
80 } else if (offset >= start) {
81
82 users.addToResults(user);
83 }
84
85 ++offset;
86 }
87
88 return users;
89 }
90
91 @Override
92 public void createUser(final String username,
93 final String rolename,
94 @Nullable final String preferredLang,
95 @Nullable final String preferredTimeZone,
96 final boolean enabled
97 ) throws SQLException, IOException, DuplicateEntityException {
98
99 checkNotNull(username, "username");
100 checkNotNull(rolename, "rolename");
101
102 final DateTime now = clock.now();
103
104 final MutableUserDto user = instantiate(MutableUserDto.class)
105 .setUsername(username)
106 .setRolename(rolename)
107 .setPreferredLang(preferredLang)
108 .setPreferredTimeZone(preferredTimeZone)
109 .setEnabled(enabled)
110 .setCreatedAt(now)
111 .setUpdatedAt(now)
112 .setRevision(1);
113
114 final UserDto old = users.putIfAbsent(username, user);
115
116 if (old != null) {
117 throw new DuplicateEntityException("username: " + username);
118 }
119 }
120
121 @Override
122 @Nullable
123 public UserDto getUser(final String username) throws SQLException, IOException {
124
125 checkNotNull(username, "username");
126
127 final MutableUserDto userDto = users.get(username);
128
129 if (userDto == null) {
130 return null;
131 }
132
133 return userDto;
134 }
135
136 @Override
137 public void setLastActiveAt(final String username, final DateTime lastActiveAt) throws SQLException, IOException {
138
139 checkNotNull(username, "username");
140 checkNotNull(lastActiveAt, "lastActiveAt");
141
142 final MutableUserDto userDto = users.get(username);
143
144 if (userDto == null) {
145 return;
146 }
147
148 userDto.setLastActiveAt(lastActiveAt);
149 }
150
151 @Override
152 public void updateUser(final String username,
153 final String rolename,
154 @Nullable final String preferredLang,
155 @Nullable final String preferredTimeZone,
156 final boolean enabled,
157 final int fromRevision
158 ) throws SQLException, IOException {
159
160 checkNotNull(username, "username");
161 checkNotNull(rolename, "rolename");
162
163 final MutableUserDto dto = users.get(username);
164
165 if (dto == null) {
166 return;
167 }
168
169 final int actualRevision = dto.getRevision();
170
171 if (actualRevision != fromRevision) {
172 throw new ConcurrentModificationException(
173 "revision should be: " + fromRevision + ", but was: " + actualRevision);
174 }
175
176 final DateTime now = clock.now();
177
178 dto.setUpdatedAt(now)
179 .setRolename(rolename)
180 .setEnabled(enabled)
181 .setRevision(fromRevision + 1);
182
183 if (preferredLang != null) {
184 dto.setPreferredLang(preferredLang);
185 }
186
187 if (preferredTimeZone != null) {
188 dto.setPreferredTimeZone(preferredTimeZone);
189 }
190 }
191
192 @Override
193 public void deleteUser(final String username) throws SQLException, IOException {
194
195 checkNotNull(username, "username");
196
197 users.remove(username);
198 }
199 }