1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.exoplatform.social.core.jpa.storage.dao.jpa;
21
22 import java.lang.reflect.Array;
23 import java.math.BigInteger;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.function.Function;
30 import java.util.stream.Collectors;
31
32 import javax.persistence.EntityExistsException;
33 import javax.persistence.NoResultException;
34 import javax.persistence.Query;
35 import javax.persistence.TypedQuery;
36
37 import org.exoplatform.commons.api.persistence.ExoTransactional;
38 import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl;
39 import org.exoplatform.commons.utils.ListAccess;
40 import org.exoplatform.services.log.ExoLogger;
41 import org.exoplatform.services.log.Log;
42 import org.exoplatform.social.core.identity.model.Identity;
43 import org.exoplatform.social.core.identity.model.Profile;
44 import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
45 import org.exoplatform.social.core.jpa.search.ExtendProfileFilter;
46 import org.exoplatform.social.core.jpa.storage.dao.IdentityDAO;
47 import org.exoplatform.social.core.jpa.storage.dao.jpa.query.ProfileQueryBuilder;
48 import org.exoplatform.social.core.jpa.storage.entity.ConnectionEntity;
49 import org.exoplatform.social.core.jpa.storage.entity.IdentityEntity;
50 import org.exoplatform.social.core.relationship.model.Relationship.Type;
51
52
53
54
55 public class IdentityDAOImpl extends GenericDAOJPAImpl<IdentityEntity, Long> implements IdentityDAO {
56
57 private static final Log LOG = ExoLogger.getLogger(IdentityDAOImpl.class);
58
59 @Override
60 public IdentityEntity create(IdentityEntity entity) {
61 IdentityEntity exists = findByProviderAndRemoteId(entity.getProviderId(), entity.getRemoteId());
62 if (exists != null) {
63 throw new EntityExistsException("Identity is existed with ProviderID=" + entity.getProviderId() + " and RemoteId=" + entity.getRemoteId());
64 }
65 return super.create(entity);
66 }
67
68 @Override
69 public IdentityEntity findByProviderAndRemoteId(String providerId, String remoteId) {
70 TypedQuery<IdentityEntity> query = getEntityManager().createNamedQuery("SocIdentity.findByProviderAndRemoteId", IdentityEntity.class);
71 query.setParameter("providerId", providerId);
72 query.setParameter("remoteId", remoteId);
73
74 try {
75 return query.getSingleResult();
76 } catch (NoResultException ex) {
77 return null;
78 }
79 }
80
81 @Override
82 public long countIdentityByProvider(String providerId) {
83 TypedQuery<Long> query = getEntityManager().createNamedQuery("SocIdentity.countIdentityByProvider", Long.class);
84 query.setParameter("providerId", providerId);
85 return query.getSingleResult();
86 }
87
88 @Override
89 public List<Long> getAllIds(int offset, int limit) {
90 TypedQuery<Long> query = getEntityManager().createNamedQuery("SocIdentity.getAllIds", Long.class);
91 if (limit > 0) {
92 query.setFirstResult(offset);
93 query.setMaxResults(limit);
94 }
95 return query.getResultList();
96 }
97
98 @Override
99 public List<Long> getAllIdsByProvider(String providerId, int offset, int limit) {
100 TypedQuery<Long> query = getEntityManager().createNamedQuery("SocIdentity.getAllIdsByProvider", Long.class);
101 query.setParameter("providerId", providerId);
102 if (limit > 0) {
103 query.setFirstResult(offset);
104 query.setMaxResults(limit);
105 }
106 return query.getResultList();
107 }
108
109 @Override
110 public ListAccess<Map.Entry<IdentityEntity, ConnectionEntity>> findAllIdentitiesWithConnections(long identityId, String sortField, char firstChar) {
111 Query listQuery = getIdentitiesQuerySortedByField(OrganizationIdentityProvider.NAME, sortField, firstChar);
112
113 TypedQuery<ConnectionEntity> connectionsQuery = getEntityManager().createNamedQuery("SocConnection.findConnectionsByIdentityIds", ConnectionEntity.class);
114
115 TypedQuery<Long> countQuery = getEntityManager().createNamedQuery("SocIdentity.countIdentitiesByProviderWithExcludedIdentity", Long.class);
116 countQuery.setParameter("providerId", OrganizationIdentityProvider.NAME);
117
118 return new IdentityWithRelationshipListAccess(identityId, listQuery, connectionsQuery, countQuery);
119 }
120
121 @Override
122 public ListAccess<IdentityEntity> findIdentities(ExtendProfileFilter filter) {
123 if (filter.getConnection() != null) {
124 Identity owner = filter.getConnection();
125 Long ownerId = Long.valueOf(owner.getId());
126 Type status = filter.getConnectionStatus();
127 List<Long> connections = getConnections(ownerId, status);
128 if (connections.isEmpty()) {
129 return new JPAListAccess<>(IdentityEntity.class);
130 } else if (filter.getIdentityIds() == null || filter.getIdentityIds().isEmpty()) {
131 filter.setIdentityIds(connections);
132 } else {
133 filter.getIdentityIds().retainAll(connections);
134 }
135 }
136
137 ProfileQueryBuilder qb = ProfileQueryBuilder.builder()
138 .withFilter(filter);
139 TypedQuery[] queries = qb.build(getEntityManager());
140
141 return new JPAListAccess<>(IdentityEntity.class, queries[0], queries[1]);
142 }
143
144 @Override
145 public List<String> getAllIdsByProviderSorted(String providerId, String sortField, char firstChar, long offset, long limit) {
146 Query query = getIdentitiesQuerySortedByField(providerId, sortField, firstChar);
147 return getResultsFromQuery(query, 0, offset, limit, String.class);
148 }
149
150 @Override
151 @ExoTransactional
152 public void setAsDeleted(long identityId) {
153 IdentityEntity entity = find(identityId);
154 if (entity != null) {
155 entity.setDeleted(true);
156 update(entity);
157 }
158 }
159
160 @Override
161 @ExoTransactional
162 public void hardDeleteIdentity(long identityId) {
163 IdentityEntity entity = find(identityId);
164 if (entity != null) {
165 delete(entity);
166 }
167 }
168
169 public List<IdentityEntity> findIdentitiesByIDs(List<?> ids) {
170 TypedQuery<IdentityEntity> query = getEntityManager().createNamedQuery("SocIdentity.findIdentitiesByIDs", IdentityEntity.class);
171 query.setParameter("ids", ids);
172 return query.getResultList();
173 }
174
175 @SuppressWarnings("unchecked")
176 private List<Long> getConnections(Long ownerId, Type status) {
177 String queryName = null;
178 Class<?> returnType = null;
179 if (status == null || status == Type.ALL) {
180 queryName = "SocConnection.getConnectionsWithoutStatus";
181 returnType = ConnectionEntity.class;
182 } else if (status == Type.INCOMING) {
183 queryName = "SocConnection.getSenderIdsByReceiverWithStatus";
184 returnType = Long.class;
185 status = Type.PENDING;
186 } else if (status == Type.OUTGOING) {
187 queryName = "SocConnection.getReceiverIdsBySenderWithStatus";
188 returnType = Long.class;
189 status = Type.PENDING;
190 } else {
191 queryName = "SocConnection.getConnectionsWithStatus";
192 returnType = ConnectionEntity.class;
193 }
194
195 Query query = getEntityManager().createNamedQuery(queryName);
196 query.setParameter("identityId", ownerId);
197 if (status != null && status != Type.ALL) {
198 query.setParameter("status", status);
199 }
200 if (returnType == Long.class) {
201 return query.getResultList();
202 } else {
203 List<Long> ids = new ArrayList<Long>();
204 List<ConnectionEntity> connectionEntities = query.getResultList();
205 for (ConnectionEntity connectionEntity : connectionEntities) {
206 if (connectionEntity.getReceiver().getId() == ownerId) {
207 ids.add(connectionEntity.getSender().getId());
208 } else if (connectionEntity.getSender().getId() == ownerId) {
209 ids.add(connectionEntity.getReceiver().getId());
210 } else {
211 LOG.warn("Neither sender neither receiver corresponds to owner with id {}. ", ownerId);
212 }
213 }
214 return ids;
215 }
216 }
217
218 public static class JPAListAccess<T> implements ListAccess<T> {
219 private final TypedQuery<T> selectQuery;
220 private final TypedQuery<Long> countQuery;
221 private final Class<T> clazz;
222
223 public JPAListAccess(Class<T> clazz) {
224 this.clazz = clazz;
225 this.selectQuery = null;
226 this.countQuery = null;
227 }
228
229 public JPAListAccess(Class<T> clazz, TypedQuery<T> selectQuery, TypedQuery<Long> countQuery) {
230 this.clazz = clazz;
231 this.selectQuery = selectQuery;
232 this.countQuery = countQuery;
233 }
234
235 @Override
236 public T[] load(int offset, int limit) throws Exception, IllegalArgumentException {
237 if (selectQuery == null) {
238 return (T[]) Array.newInstance(clazz, 0);
239 }
240 if (limit > 0 && offset >= 0) {
241 selectQuery.setFirstResult(offset);
242 selectQuery.setMaxResults(limit);
243 } else {
244 selectQuery.setMaxResults(Integer.MAX_VALUE);
245 }
246
247 List<T> list = selectQuery.getResultList();
248 if (list != null && list.size() > 0) {
249 T[] arr = (T[])Array.newInstance(clazz, list.size());
250 return list.toArray(arr);
251 } else {
252 return (T[])Array.newInstance(clazz, 0);
253 }
254 }
255
256 @Override
257 public int getSize() throws Exception {
258 if (countQuery == null) {
259 return 0;
260 }
261 return countQuery.getSingleResult().intValue();
262 }
263 }
264
265 public class IdentityWithRelationshipListAccess implements ListAccess<Map.Entry<IdentityEntity, ConnectionEntity>> {
266 private final Query identityQuery;
267 private final TypedQuery<ConnectionEntity> connectionsQuery;
268 private final TypedQuery<Long> countQuery;
269 private final long identityId;
270
271 public IdentityWithRelationshipListAccess(long identityId, Query identityQuery, TypedQuery<ConnectionEntity> connctionsQuery, TypedQuery<Long> countQuery) {
272 this.identityQuery = identityQuery;
273 this.connectionsQuery = connctionsQuery;
274 this.countQuery = countQuery;
275 this.identityId = identityId;
276 }
277
278 @SuppressWarnings("unchecked")
279 @Override
280 public Map.Entry<IdentityEntity, ConnectionEntity>[] load(int offset, int limit) throws Exception, IllegalArgumentException {
281 List<Object> ids = getResultsFromQuery(identityQuery, 1, offset, limit, Object.class);
282
283 if(ids.isEmpty()) {
284 return new Map.Entry[0];
285 }
286 List<Long> idsLong = ids.stream().map(i -> Long.parseLong(i.toString())).collect(Collectors.toList());
287 List<IdentityEntity> identitiesList = findIdentitiesByIDs(idsLong);
288 Map<Long, IdentityEntity> identitiesMap = identitiesList.stream().collect(Collectors.toMap(identity -> identity.getId(), Function.identity()));
289 connectionsQuery.setParameter("identityId", identityId);
290 connectionsQuery.setParameter("ids", idsLong);
291 connectionsQuery.setMaxResults(Integer.MAX_VALUE);
292 List<ConnectionEntity> connectionsList = connectionsQuery.getResultList();
293 Map<IdentityEntity, ConnectionEntity> map = new LinkedHashMap<IdentityEntity, ConnectionEntity>();
294 for (Long identityId : idsLong) {
295 IdentityEntity identityEntity = identitiesMap.get(identityId);
296 if (identityEntity == null) {
297 LOG.error("Can't find identity with id '{}'", identityId);
298 continue;
299 }
300 CONN: for (ConnectionEntity connectionEntity : connectionsList) {
301 if(connectionEntity.getReceiver().getId() == identityEntity.getId() || connectionEntity.getSender().getId() == identityEntity.getId()) {
302 map.put(identityEntity, connectionEntity);
303 break CONN;
304 }
305 }
306 if (!map.containsKey(identityEntity)) {
307 map.put(identityEntity, null);
308 }
309 }
310 return map.entrySet().toArray(new Map.Entry[0]);
311 }
312
313 @Override
314 public int getSize() throws Exception {
315 return countQuery.getSingleResult().intValue();
316 }
317 }
318
319 private Query getIdentitiesQuerySortedByField(String providerId, String sortField, char firstCharacter) {
320
321 String dbBoolFalse = isOrcaleDialect() || isMSSQLDialect() ? "0" : "FALSE";
322 String dbBoolTrue = isOrcaleDialect() || isMSSQLDialect() ? "1" : "TRUE";
323
324 StringBuilder queryStringBuilder =
325 isOrcaleDialect() ? new StringBuilder("SELECT to_char(identity_1.remote_id), identity_1.identity_id, to_char(identity_prop.value) \n")
326 :isMSSQLDialect() ? new StringBuilder("SELECT try_convert(varchar(200), identity_1.remote_id) as remote_id , identity_1.identity_id, try_convert(varchar(200), identity_prop.value) as identity_prop_value \n")
327 : new StringBuilder("SELECT (identity_1.remote_id), identity_1.identity_id, (identity_prop.value) \n");
328 queryStringBuilder.append(" FROM SOC_IDENTITIES identity_1 \n");
329 if (firstCharacter > 0) {
330 queryStringBuilder.append(" INNER JOIN SOC_IDENTITY_PROPERTIES identity_prop_first_char \n");
331 queryStringBuilder.append(" ON identity_1.identity_id = identity_prop_first_char.identity_id \n");
332 queryStringBuilder.append(" AND identity_prop_first_char.name = '").append(Profile.LAST_NAME).append("' \n");
333 queryStringBuilder.append(" AND (lower(identity_prop_first_char.value) like '" + Character.toLowerCase(firstCharacter) + "%')\n");
334 }
335 queryStringBuilder.append(" LEFT JOIN SOC_IDENTITY_PROPERTIES identity_prop \n");
336 queryStringBuilder.append(" ON identity_1.identity_id = identity_prop.identity_id \n");
337 queryStringBuilder.append(" AND identity_prop.name = '").append(sortField).append("' \n");
338 queryStringBuilder.append(" WHERE identity_1.provider_id = '").append(providerId).append("' \n");
339 queryStringBuilder.append(" AND identity_1.deleted = ").append(dbBoolFalse).append(" \n");
340 queryStringBuilder.append(" AND identity_1.enabled = ").append(dbBoolTrue).append(" \n");
341 queryStringBuilder.append(" ORDER BY identity_prop.value ASC");
342
343 Query query = getEntityManager().createNativeQuery(queryStringBuilder.toString());
344 return query;
345 }
346
347
348 private <T> List<T> getResultsFromQuery(Query query, int fieldIndex, long offset, long limit, Class<T> clazz) {
349 if (limit > 0) {
350 query.setMaxResults((int) limit);
351 }
352 if (offset >= 0) {
353 query.setFirstResult((int) offset);
354 }
355
356 List<?> resultList = query.getResultList();
357 List<T> result = new ArrayList<T>();
358 for (Object object : resultList) {
359 Object[] resultEntry = (Object[]) object;
360 Object resultObject = resultEntry[fieldIndex];
361 if (resultObject == null) {
362 continue;
363 }
364 result.add((T) resultObject);
365 }
366 return result;
367 }
368
369 }