View Javadoc
1   /*
2    * Copyright (C) 2003-2016 eXo Platform SAS.
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU Affero General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU Affero General Public License for more details.
13   *
14   * You should have received a copy of the GNU Affero General Public License
15   * along with this program. If not, see <http://www.gnu.org/licenses/>.
16   */
17  
18  package org.exoplatform.social.core.jpa.storage;
19  
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.Date;
24  import java.util.HashSet;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.Set;
28  
29  import org.apache.commons.lang.StringUtils;
30  
31  import org.exoplatform.commons.api.persistence.ExoTransactional;
32  import org.exoplatform.services.log.ExoLogger;
33  import org.exoplatform.services.log.Log;
34  import org.exoplatform.social.core.identity.model.Identity;
35  import org.exoplatform.social.core.identity.model.Profile;
36  import org.exoplatform.social.core.identity.provider.SpaceIdentityProvider;
37  import org.exoplatform.social.core.jpa.search.XSpaceFilter;
38  import org.exoplatform.social.core.jpa.storage.dao.*;
39  import org.exoplatform.social.core.jpa.storage.dao.jpa.query.SpaceQueryBuilder;
40  import org.exoplatform.social.core.jpa.storage.entity.SpaceEntity;
41  import org.exoplatform.social.core.jpa.storage.entity.SpaceMemberEntity;
42  import org.exoplatform.social.core.jpa.storage.entity.SpaceMemberEntity.Status;
43  import org.exoplatform.social.core.service.LinkProvider;
44  import org.exoplatform.social.core.space.SpaceFilter;
45  import org.exoplatform.social.core.space.SpaceUtils;
46  import org.exoplatform.social.core.space.model.Space;
47  import org.exoplatform.social.core.storage.SpaceStorageException;
48  import org.exoplatform.social.core.storage.api.IdentityStorage;
49  import org.exoplatform.social.core.storage.api.SpaceStorage;
50  import org.exoplatform.social.core.storage.exception.NodeNotFoundException;
51  import org.exoplatform.social.core.storage.impl.SpaceStorageImpl;
52  
53  public class RDBMSSpaceStorageImpl implements SpaceStorage {
54  
55    /** Logger */
56    private static final Log     LOG = ExoLogger.getLogger(RDBMSSpaceStorageImpl.class);
57  
58    private static final int     BATCH_SIZE = 100;
59  
60    private SpaceDAO             spaceDAO;
61  
62    private SpaceMemberDAO       spaceMemberDAO;
63  
64    private IdentityDAO          identityDAO;
65  
66    private IdentityStorage     identityStorage;
67  
68    private ActivityDAO         activityDAO;
69  
70    public RDBMSSpaceStorageImpl(SpaceDAO spaceDAO,
71                                 SpaceMemberDAO spaceMemberDAO,
72                                 RDBMSIdentityStorageImpl identityStorage,
73                                 IdentityDAO identityDAO,
74                                 ActivityDAO activityDAO) {
75      this.spaceDAO = spaceDAO;
76      this.identityStorage = identityStorage;
77      this.spaceMemberDAO = spaceMemberDAO;
78      this.identityDAO = identityDAO;
79      this.activityDAO = activityDAO;
80    }
81  
82    @Override
83    @ExoTransactional
84    public void deleteSpace(String id) throws SpaceStorageException {
85      SpaceEntity entity = spaceDAO.find(Long.parseLong(id));
86      if (entity != null) {
87        Identity spaceIdentity = identityStorage.findIdentity(SpaceIdentityProvider.NAME, entity.getPrettyName());
88        if (spaceIdentity == null) {
89          LOG.warn("Space with pretty name '{}' hasn't a related identity", entity.getPrettyName());
90        } else {
91          identityDAO.hardDeleteIdentity(Long.parseLong(spaceIdentity.getId()));
92          activityDAO.deleteActivitiesByOwnerId(spaceIdentity.getId());
93        }
94        spaceDAO.delete(entity);
95        LOG.debug("Space {} removed", entity.getPrettyName());
96      }
97    }
98  
99    @Override
100   public List<Space> getAccessibleSpaces(String userId) throws SpaceStorageException {
101     return getAccessibleSpaces(userId, 0, -1);
102   }
103 
104   @Override
105   public List<Space> getAccessibleSpaces(String userId, long offset, long limit) throws SpaceStorageException {
106     return getAccessibleSpacesByFilter(userId, null, offset, limit);
107   }
108 
109   @Override
110   public List<Space> getAccessibleSpacesByFilter(String userId, SpaceFilter spaceFilter, long offset, long limit) {
111     return getSpaces(userId, Arrays.asList(Status.MANAGER, Status.MEMBER), spaceFilter, offset, limit);
112   }
113 
114   @Override
115   public int getAccessibleSpacesByFilterCount(String userId, SpaceFilter spaceFilter) {
116     return getSpacesCount(userId, Arrays.asList(Status.MANAGER, Status.MEMBER), spaceFilter);
117   }
118 
119   @Override
120   public int getAccessibleSpacesCount(String userId) throws SpaceStorageException {
121     return getAccessibleSpacesByFilterCount(userId, null);
122   }
123 
124   @Override
125   public List<Space> getAllSpaces() throws SpaceStorageException {
126     return getSpaces(0, -1);
127   }
128 
129   @Override
130   public int getAllSpacesByFilterCount(SpaceFilter spaceFilter) {
131     return getSpacesCount(null, null, spaceFilter);
132   }
133 
134   @Override
135   public int getAllSpacesCount() throws SpaceStorageException {
136     return getAllSpacesByFilterCount(null);
137   }
138 
139   @Override
140   public List<Space> getEditableSpaces(String userId) throws SpaceStorageException {
141     return getEditableSpaces(userId, 0, -1);
142   }
143 
144   @Override
145   public List<Space> getEditableSpaces(String userId, long offset, long limit) throws SpaceStorageException {
146     return getEditableSpacesByFilter(userId, null, offset, limit);
147   }
148 
149   @Override
150   public List<Space> getEditableSpacesByFilter(String userId, SpaceFilter spaceFilter, long offset, long limit) {
151     return getSpaces(userId, Arrays.asList(Status.MANAGER), spaceFilter, offset, limit);
152   }
153 
154   @Override
155   public int getEditableSpacesByFilterCount(String userId, SpaceFilter spaceFilter) {
156     return getSpacesCount(userId, Arrays.asList(Status.MANAGER), spaceFilter);
157   }
158 
159   @Override
160   public int getEditableSpacesCount(String userId) throws SpaceStorageException {
161     return getEditableSpacesByFilterCount(userId, null);
162   }
163 
164   @Override
165   public List<Space> getInvitedSpaces(String userId) throws SpaceStorageException {
166     return getInvitedSpaces(userId, 0, -1);
167   }
168 
169   @Override
170   public List<Space> getInvitedSpaces(String userId, long offset, long limit) throws SpaceStorageException {
171     return getInvitedSpacesByFilter(userId, null, offset, limit);
172   }
173 
174   @Override
175   public List<Space> getInvitedSpacesByFilter(String userId, SpaceFilter spaceFilter, long offset, long limit) {
176     return getSpaces(userId, Arrays.asList(Status.INVITED), spaceFilter, offset, limit);
177   }
178 
179   @Override
180   public int getInvitedSpacesByFilterCount(String userId, SpaceFilter spaceFilter) {
181     return getSpacesCount(userId, Arrays.asList(Status.INVITED), spaceFilter);
182   }
183 
184   @Override
185   public int getInvitedSpacesCount(String userId) throws SpaceStorageException {
186     return getInvitedSpacesByFilterCount(userId, null);
187   }
188 
189   @Override
190   public List<Space> getLastAccessedSpace(SpaceFilter spaceFilter, int offset, int limit) throws SpaceStorageException {
191     XSpaceFilter xFilter = new XSpaceFilter();
192     xFilter.setSpaceFilter(spaceFilter);
193     xFilter.setLastAccess(true);
194     return getMemberSpacesByFilter(spaceFilter.getRemoteId(), xFilter, offset, limit);
195   }
196 
197   @Override
198   public int getLastAccessedSpaceCount(SpaceFilter spaceFilter) throws SpaceStorageException {
199     return getMemberSpacesByFilterCount(spaceFilter.getRemoteId(), spaceFilter);
200   }
201 
202   @Override
203   public List<Space> getLastSpaces(int limit) {
204     List<SpaceEntity> entities = spaceDAO.getLastSpaces(limit);
205     return buildList(entities);
206   }
207 
208   @Override
209   public List<String> getMemberSpaceIds(String identityId, int offset, int limit) throws SpaceStorageException {
210     Identity identity = identityStorage.findIdentityById(identityId);
211     List<Long> spaceIds = spaceMemberDAO.getSpacesIdsByUserName(identity.getRemoteId(), offset, limit);
212 
213     List<String> ids = new LinkedList<>();
214     if (spaceIds != null && !spaceIds.isEmpty()) {
215       for (Long spaceId : spaceIds) {
216         ids.add(String.valueOf(spaceId));
217       }
218     }
219     return ids;
220   }
221 
222   @Override
223   public List<Space> getMemberSpaces(String userId) throws SpaceStorageException {
224     return getMemberSpaces(userId, 0, -1);
225   }
226 
227   @Override
228   public List<Space> getMemberSpaces(String userId, long offset, long limit) throws SpaceStorageException {
229     return getMemberSpacesByFilter(userId, null, offset, limit);
230   }
231 
232   @Override
233   public List<Space> getMemberSpacesByFilter(String userId, SpaceFilter spaceFilter, long offset, long limit) {
234     return getSpaces(userId, Arrays.asList(Status.MEMBER), spaceFilter, offset, limit);
235   }
236 
237   @Override
238   public int getMemberSpacesByFilterCount(String userId, SpaceFilter spaceFilter) {
239     return getSpacesCount(userId, Arrays.asList(Status.MEMBER), spaceFilter);
240   }
241 
242   @Override
243   public int getMemberSpacesCount(String userId) throws SpaceStorageException {
244     return getMemberSpacesByFilterCount(userId, null);
245   }
246 
247   @Override
248   public int getNumberOfMemberPublicSpaces(String userId) {
249     XSpaceFilter filter = new XSpaceFilter();
250     filter.setNotHidden(true);
251     return getSpacesCount(userId, Arrays.asList(Status.MEMBER), filter);
252   }
253 
254   @Override
255   public List<Space> getPendingSpaces(String userId) throws SpaceStorageException {
256     return getPendingSpaces(userId, 0, -1);
257   }
258 
259   @Override
260   public List<Space> getPendingSpaces(String userId, long offset, long limit) throws SpaceStorageException {
261     return getPendingSpacesByFilter(userId, null, offset, limit);
262   }
263 
264   @Override
265   public List<Space> getPendingSpacesByFilter(String userId, SpaceFilter spaceFilter, long offset, long limit) {
266     return getSpaces(userId, Arrays.asList(Status.PENDING), spaceFilter, offset, limit);
267   }
268 
269   @Override
270   public int getPendingSpacesByFilterCount(String userId, SpaceFilter spaceFilter) {
271     return getSpacesCount(userId, Arrays.asList(Status.PENDING), spaceFilter);
272   }
273 
274   @Override
275   public int getPendingSpacesCount(String userId) throws SpaceStorageException {
276     return getPendingSpacesByFilterCount(userId, null);
277   }
278 
279   @Override
280   public List<Space> getPublicSpaces(String userId) throws SpaceStorageException {
281     return getPublicSpaces(userId, 0, -1);
282   }
283 
284   @Override
285   public List<Space> getPublicSpaces(String userId, long offset, long limit) throws SpaceStorageException {
286     return getPublicSpacesByFilter(userId, null, offset, limit);
287   }
288 
289   @Override
290   public List<Space> getPublicSpacesByFilter(String userId, SpaceFilter spaceFilter, long offset, long limit) {
291     XSpaceFilter filter = new XSpaceFilter();
292     filter.setSpaceFilter(spaceFilter);
293     filter.setPublic(userId);
294     return getSpacesByFilter(filter, offset, limit);
295   }
296 
297   @Override
298   public int getPublicSpacesByFilterCount(String userId, SpaceFilter spaceFilter) {
299     XSpaceFilter filter = new XSpaceFilter();
300     filter.setSpaceFilter(spaceFilter);
301     filter.setPublic(userId);
302     return getSpacesCount(null, null, filter);
303   }
304 
305   @Override
306   public int getPublicSpacesCount(String userId) throws SpaceStorageException {
307     return getPublicSpacesByFilterCount(userId, null);
308   }
309 
310   @Override
311   public Space getSpaceByDisplayName(String spaceDisplayName) throws SpaceStorageException {
312     SpaceEntity entity = spaceDAO.getSpaceByDisplayName(spaceDisplayName);
313     return fillSpaceFromEntity(entity);
314   }
315 
316   @Override
317   public Space getSpaceByGroupId(String groupId) throws SpaceStorageException {
318     SpaceEntity entity = spaceDAO.getSpaceByGroupId(groupId);
319     return fillSpaceFromEntity(entity);
320   }
321 
322   @Override
323   @ExoTransactional
324   public Space getSpaceById(String id) throws SpaceStorageException {
325     Long spaceId;
326     try {
327       spaceId = Long.parseLong(id);
328     } catch (Exception ex) {
329       return null;
330     }
331     SpaceEntity entity = spaceDAO.find(spaceId);
332     return fillSpaceFromEntity(entity);
333   }
334 
335   @Override
336   @ExoTransactional
337   public Space getSpaceByPrettyName(String spacePrettyName) throws SpaceStorageException {
338     SpaceEntity entity = spaceDAO.getSpaceByPrettyName(spacePrettyName);
339     return fillSpaceFromEntity(entity);
340   }
341 
342   @Override
343   public Space getSpaceByUrl(String url) throws SpaceStorageException {
344     SpaceEntity entity = spaceDAO.getSpaceByURL(url);
345     return fillSpaceFromEntity(entity);
346   }
347 
348   @Override
349   public Space getSpaceSimpleById(String id) throws SpaceStorageException {
350     Long spaceId;
351     try {
352       spaceId = Long.parseLong(id);
353     } catch (Exception ex) {
354       return null;
355     }
356     SpaceEntity entity = spaceDAO.find(spaceId);
357     Space space = new Space();
358     return fillSpaceSimpleFromEntity(entity, space);
359   }
360 
361   @Override
362   public List<Space> getSpaces(long offset, long limit) throws SpaceStorageException {
363     return getSpacesByFilter(null, offset, limit);
364   }
365 
366   @Override
367   public List<Space> getSpacesByFilter(SpaceFilter spaceFilter, long offset, long limit) {
368     return getSpaces(null, null, spaceFilter, offset, limit);
369   }
370 
371   @Override
372   public List<Space> getUnifiedSearchSpaces(String userId,
373                                             SpaceFilter spaceFilter,
374                                             long offset,
375                                             long limit) throws SpaceStorageException {
376 //    XSpaceFilter xFilter = new XSpaceFilter();
377 //    xFilter.setSpaceFilter(spaceFilter).setUnifiedSearch(true);
378 //    return getSpacesByFilter(xFilter, offset, limit);
379     throw new UnsupportedOperationException();
380   }
381 
382   @Override
383   public int getUnifiedSearchSpacesCount(String userId, SpaceFilter spaceFilter) throws SpaceStorageException {
384     XSpaceFilter xFilter = new XSpaceFilter();
385     xFilter.setSpaceFilter(spaceFilter).setUnifiedSearch(true);
386     return getSpacesCount(null, null, xFilter);
387   }
388 
389   @Override
390   public List<Space> getVisibleSpaces(String userId, SpaceFilter spaceFilter) throws SpaceStorageException {
391     return getVisibleSpaces(userId, spaceFilter, 0, -1);
392   }
393 
394   @Override
395   public List<Space> getVisibleSpaces(String userId,
396                                       SpaceFilter spaceFilter,
397                                       long offset,
398                                       long limit) throws SpaceStorageException {
399     XSpaceFilter xFilter = new XSpaceFilter();
400     xFilter.setSpaceFilter(spaceFilter).setRemoteId(userId);
401     xFilter.addStatus(Status.MEMBER, Status.MANAGER, Status.INVITED);
402     xFilter.setIncludePrivate(true);
403     return getSpacesByFilter(xFilter, offset, limit);
404   }
405 
406   @Override
407   public int getVisibleSpacesCount(String userId, SpaceFilter spaceFilter) throws SpaceStorageException {
408     XSpaceFilter xFilter = new XSpaceFilter();
409     xFilter.setSpaceFilter(spaceFilter).setRemoteId(userId);
410     xFilter.addStatus(Status.MEMBER, Status.MANAGER, Status.INVITED);
411     xFilter.setIncludePrivate(true);
412     return getSpacesCount(userId, null, xFilter);
413   }
414 
415   @Override
416   public List<Space> getVisitedSpaces(SpaceFilter spaceFilter, int offset, int limit) throws SpaceStorageException {
417     XSpaceFilter xFilter = new XSpaceFilter();
418     xFilter.setSpaceFilter(spaceFilter);
419     xFilter.setVisited(true);
420     return getMemberSpacesByFilter(spaceFilter.getRemoteId(), xFilter, offset, limit);
421   }
422 
423   @Override
424   @ExoTransactional
425   public void renameSpace(Space space, String newDisplayName) throws SpaceStorageException {
426     renameSpace(null, space, newDisplayName);
427   }
428 
429   @Override
430   public void ignoreSpace(String spaceId, String userId) {
431     SpaceMemberEntity entity = spaceMemberDAO.getSpaceMemberShip(userId, Long.parseLong(spaceId), null);
432     SpaceEntity spaceEntity = spaceDAO.find(Long.parseLong(spaceId));
433     if (entity == null) {
434       entity = new SpaceMemberEntity();
435       entity.setSpace(spaceEntity);
436       entity.setUserId(userId);
437       entity.setStatus(Status.IGNORED);
438       spaceMemberDAO.create(entity);
439     } else {
440       entity.setStatus(Status.IGNORED);
441       spaceMemberDAO.update(entity);
442     }
443   }
444 
445   @Override
446   public boolean isSpaceIgnored(String spaceId, String userId) {
447     SpaceMemberEntity entity = spaceMemberDAO.getSpaceMemberShip(userId, Long.parseLong(spaceId), Status.IGNORED);
448     return entity != null;
449   }
450   
451   @Override
452   @ExoTransactional
453   public void renameSpace(String remoteId, Space space, String newDisplayName) throws SpaceStorageException {
454     SpaceEntity entity;
455 
456     try {
457       String oldPrettyName = space.getPrettyName();
458 
459       space.setDisplayName(newDisplayName);
460       space.setPrettyName(space.getDisplayName());
461       space.setUrl(SpaceUtils.cleanString(newDisplayName));
462 
463       entity = spaceDAO.find(Long.parseLong(space.getId()));
464       EntityConverterUtils.buildFrom(space, entity);
465 
466       // change profile of space
467       Identity identitySpace = identityStorage.findIdentity(SpaceIdentityProvider.NAME, oldPrettyName);
468 
469       if (identitySpace != null) {
470         Profile profileSpace = identitySpace.getProfile();
471         profileSpace.setProperty(Profile.URL, space.getUrl());
472 
473         identityStorage.saveProfile(profileSpace);
474 
475         identitySpace.setRemoteId(space.getPrettyName());
476         // TODO remove this after finish RDBMSIdentityStorage
477         renameIdentity(identitySpace);
478       }
479 
480       //
481       LOG.debug(String.format("Space %s (%s) saved", space.getPrettyName(), space.getId()));
482 
483     } catch (NodeNotFoundException e) {
484       throw new SpaceStorageException(SpaceStorageException.Type.FAILED_TO_RENAME_SPACE, e.getMessage(), e);
485     }
486   }
487 
488   @Override
489   @ExoTransactional
490   public void saveSpace(Space space, boolean isNew) throws SpaceStorageException {
491     if (isNew) {
492       SpaceEntity entity = new SpaceEntity();
493       EntityConverterUtils.buildFrom(space, entity);
494 
495       //
496       spaceDAO.create(entity);
497       space.setId(String.valueOf(entity.getId()));
498     } else {
499       Long id = Long.parseLong(space.getId());
500       SpaceEntity entity = spaceDAO.find(id);
501 
502       if (entity != null) {
503         EntityConverterUtils.buildFrom(space, entity);
504         //
505         spaceDAO.update(entity);
506       } else {
507         throw new SpaceStorageException(SpaceStorageException.Type.FAILED_TO_SAVE_SPACE);
508       }
509     }
510 
511     LOG.debug("Space {} ({}) saved", space.getPrettyName(), space.getId());
512   }
513 
514   @Override
515   @ExoTransactional
516   public void updateSpaceAccessed(String remoteId, Space space) throws SpaceStorageException {
517     SpaceMemberEntity member = spaceMemberDAO.getSpaceMemberShip(remoteId, Long.parseLong(space.getId()), Status.MEMBER);
518     if (member != null) {
519       member.setLastAccess(new Date());
520       // consider visited if access after create time more than 2s
521       if (!member.isVisited()) {
522         member.setVisited((member.getLastAccess().getTime() - member.getSpace().getCreatedDate().getTime()) >= 2000);
523       }
524     }
525     spaceMemberDAO.update(member);
526   }
527 
528   private String[] getSpaceMembers(long spaceId, SpaceMemberEntity.Status status) {
529     int countSpaceMembers = spaceMemberDAO.countSpaceMembers(spaceId, status);
530     if (countSpaceMembers == 0) {
531       return new String[0];
532     }
533     List<String> membersList = new ArrayList<>();
534     int offset = 0;
535     while (offset < countSpaceMembers) {
536       Collection<String> spaceMembers = spaceMemberDAO.getSpaceMembers(spaceId, status, offset, BATCH_SIZE);
537       for (String username : spaceMembers) {
538         if (StringUtils.isBlank(username)) {
539           continue;
540         }
541         membersList.add(username);
542       }
543       offset += BATCH_SIZE;
544     }
545     if (membersList.size() < countSpaceMembers) {
546       LOG.warn("Space members count '{}' is different from retrieved space members from database {}",
547                countSpaceMembers,
548                membersList.size());
549     }
550     return membersList.toArray(new String[0]);
551   }
552 
553   /**
554    * Fills {@link Space}'s properties to {@link SpaceEntity}'s.
555    *
556    * @param entity the space entity
557    */
558   private Space fillSpaceFromEntity(SpaceEntity entity) {
559     if (entity == null) {
560       return null;
561     }
562     Space space = new Space();
563     fillSpaceSimpleFromEntity(entity, space);
564 
565     space.setPendingUsers(getSpaceMembers(entity.getId(), Status.PENDING));
566     space.setInvitedUsers(getSpaceMembers(entity.getId(), Status.INVITED));
567 
568     //
569     String[] members = getSpaceMembers(entity.getId(), Status.MEMBER);
570     String[] managers = getSpaceMembers(entity.getId(), Status.MANAGER);
571 
572     //
573     Set<String> membersList = new HashSet<String>();
574     if (members != null)
575       membersList.addAll(Arrays.asList(members));
576     if (managers != null)
577       membersList.addAll(Arrays.asList(managers));
578 
579     //
580     space.setMembers(membersList.toArray(new String[] {}));
581     space.setManagers(managers);
582     return space;
583   }
584 
585   /**
586    * Add this method to resolve SOC-3439
587    *
588    * @param identity
589    * @throws NodeNotFoundException
590    */
591   private void renameIdentity(Identity identity) throws NodeNotFoundException {
592     identityStorage.saveIdentity(identity);
593     /*
594      * ProviderEntity providerEntity =
595      * getProviderRoot().getProvider(identity.getProviderId()); // Move identity
596      * IdentityEntity identityEntity = _findById(IdentityEntity.class,
597      * identity.getId());
598      * providerEntity.getIdentities().put(identity.getRemoteId(),
599      * identityEntity); identityEntity.setRemoteId(identity.getRemoteId());
600      */
601   }
602 
603   private List<Space> getSpaces(String userId, List<Status> status, SpaceFilter spaceFilter, long offset, long limit) {
604     XSpaceFilter filter = new XSpaceFilter();
605     filter.setSpaceFilter(spaceFilter);
606     if (userId != null && status != null) {
607       filter.setRemoteId(userId);
608       filter.addStatus(status.toArray(new Status[status.size()]));
609     }
610 
611     if (filter.isUnifiedSearch()) {
612       //return spaceSearchConnector.search(filter, offset, limit);
613       throw new UnsupportedOperationException();
614     } else {
615       SpaceQueryBuilder query = SpaceQueryBuilder.builder().filter(filter).offset(offset).limit(limit);
616       List<SpaceEntity> entities = query.build().getResultList();
617       return buildList(entities);
618     }
619   }
620 
621   private int getSpacesCount(String userId, List<Status> status, SpaceFilter spaceFilter) {
622     XSpaceFilter filter = new XSpaceFilter();
623     filter.setSpaceFilter(spaceFilter);
624     if (userId != null && status != null) {
625       filter.setRemoteId(userId);
626       filter.addStatus(status.toArray(new Status[status.size()]));
627     }
628 
629     if (filter.isUnifiedSearch()) {
630 //      return spaceSearchConnector.count(filter);
631       throw new UnsupportedOperationException();
632     } else {
633       SpaceQueryBuilder query = SpaceQueryBuilder.builder().filter(filter);
634       return query.buildCount().getSingleResult().intValue();
635     }
636   }
637 
638   private List<Space> buildList(List<SpaceEntity> spaceEntities) {
639     List<Space> spaces = new LinkedList<>();
640     if (spaceEntities != null) {
641       for (SpaceEntity entity : spaceEntities) {
642         Space space = fillSpaceFromEntity(entity);
643         spaces.add(space);
644       }
645     }
646     return spaces;
647   }
648 
649   /**
650    * Fills {@link Space}'s properties to {@link SpaceEntity}'s.
651    *
652    * @param entity the space entity from chromattic
653    * @param space the space pojo for services
654    */
655   private Space fillSpaceSimpleFromEntity(SpaceEntity entity, Space space) {
656     space.setApp(StringUtils.join(entity.getApp(), ","));
657     space.setId(String.valueOf(entity.getId()));
658     space.setDisplayName(entity.getDisplayName());
659     space.setPrettyName(entity.getPrettyName());
660     if (entity.getRegistration() != null) {
661       space.setRegistration(entity.getRegistration().name().toLowerCase());
662     }
663     space.setDescription(entity.getDescription());
664     space.setTemplate(entity.getTemplate());
665     if (entity.getVisibility() != null) {
666       space.setVisibility(entity.getVisibility().name().toLowerCase());
667     }
668     if (entity.getPriority() != null) {
669       switch (entity.getPriority()) {
670         case HIGH:
671           space.setPriority(Space.HIGH_PRIORITY);
672           break;
673         case INTERMEDIATE:
674           space.setPriority(Space.INTERMEDIATE_PRIORITY);
675           break;
676         case LOW:
677           space.setPriority(Space.LOW_PRIORITY);
678           break;
679         default:
680           space.setPriority(null);
681       }
682     }
683     space.setGroupId(entity.getGroupId());
684     space.setUrl(entity.getUrl());
685     space.setCreatedTime(entity.getCreatedDate().getTime());
686 
687     if (entity.getAvatarLastUpdated() != null) {
688       try {
689         space.setAvatarUrl(LinkProvider.buildAvatarURL(SpaceIdentityProvider.NAME, space.getPrettyName()));
690       } catch (Exception e) {
691         LOG.warn("Failed to build avatar url: " + e.getMessage());
692       }
693       space.setAvatarLastUpdated(entity.getAvatarLastUpdated().getTime());
694     }
695     if (entity.getBannerLastUpdated() != null) {
696       try {
697         space.setBannerUrl(LinkProvider.buildBannerURL(SpaceIdentityProvider.NAME, space.getPrettyName()));
698       } catch (Exception e) {
699         LOG.warn("Failed to build Banner url: " + e.getMessage());
700       }
701       space.setBannerLastUpdated(entity.getBannerLastUpdated().getTime());
702     }
703     return space;
704   }
705 
706   public void setIdentityStorage(IdentityStorage identityStorage) {
707     this.identityStorage = identityStorage;
708   }
709 
710 }