View Javadoc
1   /*
2    * Copyright (C) 2003-2011 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.storage.impl;
19  
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.LinkedHashMap;
25  import java.util.LinkedHashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.NavigableMap;
29  import java.util.Random;
30  import java.util.Set;
31  import java.util.TreeMap;
32  
33  import javax.jcr.Node;
34  import javax.jcr.NodeIterator;
35  import javax.jcr.Session;
36  
37  import org.chromattic.api.query.Ordering;
38  import org.chromattic.api.query.QueryBuilder;
39  import org.chromattic.api.query.QueryResult;
40  import org.chromattic.core.query.QueryImpl;
41  
42  import org.exoplatform.commons.notification.impl.AbstractService;
43  import org.exoplatform.commons.utils.CommonsUtils;
44  import org.exoplatform.commons.utils.ListAccess;
45  import org.exoplatform.container.PortalContainer;
46  import org.exoplatform.services.log.ExoLogger;
47  import org.exoplatform.services.log.Log;
48  import org.exoplatform.social.core.chromattic.entity.DisabledEntity;
49  import org.exoplatform.social.core.chromattic.entity.IdentityEntity;
50  import org.exoplatform.social.core.chromattic.entity.ProfileEntity;
51  import org.exoplatform.social.core.chromattic.entity.RelationshipEntity;
52  import org.exoplatform.social.core.chromattic.entity.RelationshipListEntity;
53  import org.exoplatform.social.core.identity.model.Identity;
54  import org.exoplatform.social.core.identity.model.Profile;
55  import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
56  import org.exoplatform.social.core.manager.RelationshipManager;
57  import org.exoplatform.social.core.profile.ProfileFilter;
58  import org.exoplatform.social.core.profile.ProfileLoader;
59  import org.exoplatform.social.core.relationship.model.Relationship;
60  import org.exoplatform.social.core.storage.IdentityStorageException;
61  import org.exoplatform.social.core.storage.RelationshipStorageException;
62  import org.exoplatform.social.core.storage.api.ActivityStorage;
63  import org.exoplatform.social.core.storage.api.IdentityStorage;
64  import org.exoplatform.social.core.storage.api.RelationshipStorage;
65  import org.exoplatform.social.core.storage.cache.CachedActivityStorage;
66  import org.exoplatform.social.core.storage.exception.NodeNotFoundException;
67  import org.exoplatform.social.core.storage.query.JCRProperties;
68  import org.exoplatform.social.core.storage.query.WhereExpression;
69  import org.exoplatform.social.core.storage.streams.StreamInvocationHelper;
70  
71  /**
72   * @author <a href="mailto:alain.defrance@exoplatform.com">Alain Defrance</a>
73   * @version $Revision$
74   */
75  public class RelationshipStorageImpl extends AbstractStorage implements RelationshipStorage {
76  
77    /** Logger */
78    private static final Log LOG = ExoLogger.getLogger(RelationshipStorage.class);
79  
80    private final IdentityStorage identityStorage;
81    private RelationshipManager relationshipManager;
82    private RelationshipStorage relationshipStorage;
83    private ActivityStorage activityStorage;
84  
85    public RelationshipStorageImpl(IdentityStorage identityStorage) {
86     this.identityStorage = identityStorage;
87    }
88  
89    private enum Origin { FROM, TO }
90  
91    private RelationshipManager getRelationshipManager() {
92      
93      if (relationshipManager == null) {
94        PortalContainer container = PortalContainer.getInstance();
95        this.relationshipManager  = (RelationshipManager) container.getComponentInstanceOfType(RelationshipManager.class);
96      }
97      return relationshipManager;
98    }
99  
100   private ActivityStorage getCachedActivityStorage() {
101     if (activityStorage == null) {
102       activityStorage = CommonsUtils.getService(ActivityStorage.class);
103     }
104     return activityStorage;
105   }
106   
107   private void clearActivityStorageCache() {
108     if (getCachedActivityStorage() instanceof CachedActivityStorage) {
109       ((CachedActivityStorage) getCachedActivityStorage()).clearCache();
110     }
111   }
112 
113   private void putRelationshipToList(List<Relationship> relationships, RelationshipListEntity list) {
114     if (list != null) {
115       for (Map.Entry<String, RelationshipEntity> entry : list.getRelationships().entrySet()) {
116         Relationship relationship = new Relationship(entry.getValue().getId());
117 
118         RelationshipEntity relationshipEntity = entry.getValue();
119         IdentityEntity senderEntity = relationshipEntity.getFrom();
120         IdentityEntity receiverEntity = relationshipEntity.getTo();
121         //
122         if (_getMixin(senderEntity, DisabledEntity.class, false) != null ||
123             _getMixin(receiverEntity, DisabledEntity.class, false) != null) {
124           continue;
125         }
126         Identity sender = new Identity(senderEntity.getId());
127         sender.setRemoteId(senderEntity.getRemoteId());
128         sender.setProviderId(senderEntity.getProviderId());
129         ProfileEntity senderProfileEntity = senderEntity.getProfile();
130 
131         if (senderProfileEntity != null) {
132           loadProfile(sender);
133         }
134 
135         Identity receiver = new Identity(receiverEntity.getId());
136         receiver.setRemoteId(receiverEntity.getRemoteId());
137         receiver.setProviderId(receiverEntity.getProviderId());
138         ProfileEntity receiverProfileEntity = receiverEntity.getProfile();
139 
140         if (receiverProfileEntity != null) {
141           loadProfile(receiver);
142         }
143 
144         relationship.setSender(sender);
145         relationship.setReceiver(receiver);
146 
147         if (SENDER.equals(entry.getValue().getParent().getName()) ||
148             RECEIVER.equals(entry.getValue().getParent().getName())) {
149           relationship.setStatus(Relationship.Type.PENDING);
150         }
151         else {
152           relationship.setStatus(Relationship.Type.CONFIRMED);
153         }
154 
155         // TODO : IGNORED
156 
157         relationships.add(relationship);
158       }
159     }
160   }
161 
162   private void putReceiverRelationshipToList(List<Relationship> relationships, RelationshipListEntity list, Identity receiver) {
163     if (list != null) {
164       for (Map.Entry<String, RelationshipEntity> entry : list.getRelationships().entrySet()) {
165         Relationship relationship = new Relationship(entry.getValue().getId());
166 
167         RelationshipEntity relationshipEntity = entry.getValue();
168         IdentityEntity senderEntity = relationshipEntity.getFrom();
169         if (senderEntity.getId().equals(receiver.getId())) {
170           senderEntity = relationshipEntity.getTo();
171         }
172 
173         Identity sender = new Identity(senderEntity.getId());
174         sender.setRemoteId(senderEntity.getRemoteId());
175         sender.setProviderId(senderEntity.getProviderId());
176         ProfileEntity senderProfileEntity = senderEntity.getProfile();
177 
178         if (senderProfileEntity != null) {
179           loadProfile(sender);
180         }
181 
182         if (receiver.getProfile() != null) {
183           loadProfile(receiver);
184         }
185 
186         relationship.setSender(sender);
187         relationship.setReceiver(receiver);
188         relationship.setStatus(Relationship.Type.PENDING);
189 
190        relationships.add(relationship);
191       }
192     }
193   }
194   
195   private void loadProfile(final Identity identity) {
196     ProfileLoader loader = new ProfileLoader() {
197       public Profile load() throws IdentityStorageException {
198         Profile profile = new Profile(identity);
199         return identityStorage.loadProfile(profile);
200       }
201     };
202     identity.setProfileLoader(loader);
203   }
204 
205   private List<Identity> getIdentitiesFromRelationship(Iterator<RelationshipEntity> it, Origin origin, long offset, long limit) {
206 
207     //
208     Set<Identity> identities = new LinkedHashSet<Identity>();
209     int i = 0;
210 
211     _skip(it, offset);
212 
213     Identity identity = null;
214     while (it.hasNext()) {
215 
216       RelationshipEntity relationshipEntity = it.next();
217       IdentityEntity identityEntity;
218 
219       switch (origin) {
220 
221         case FROM:
222           identityEntity = relationshipEntity.getFrom();
223           identity = createIdentityFromEntity(identityEntity);
224           
225           if (identity.isEnable()) {
226             identities.add(identity);
227           }
228           break;
229 
230         case TO:
231           identityEntity = relationshipEntity.getTo();
232           identity = createIdentityFromEntity(identityEntity);
233 
234           if (identity.isEnable()) {
235             identities.add(identity);
236           }
237           break;
238       }
239 
240       if (limit != -1 && limit > 0 && ++i >= limit) {
241         break;
242       }
243 
244     }
245 
246     return new ArrayList<Identity>(identities);
247   }
248 
249   private List<Identity> getIdentitiesFromRelationship(Iterator<RelationshipEntity> it, Identity current, long offset, long limit) {
250     //
251     Set<Identity> identities = new LinkedHashSet<Identity>();
252     int i = 0;
253 
254     _skip(it, offset);
255 
256     Identity identity = null;
257     while (it.hasNext()) {
258       RelationshipEntity relationshipEntity = it.next();
259       
260       IdentityEntity entity = relationshipEntity.getFrom();
261       if (entity.getId().equals(current.getId())) {
262         entity = relationshipEntity.getTo();
263       }
264       
265       identity = createIdentityFromEntity(entity);
266       if (identity.isEnable()) {
267         identities.add(identity);
268         if (limit != -1 && limit > 0 && ++i >= limit) {
269           break;
270         }
271       }
272 
273     }
274 
275     return new ArrayList<Identity>(identities);
276   }
277   
278   private Identity createIdentityFromEntity(IdentityEntity entity) {
279 
280     Identity identity = identityStorage.findIdentityById(entity.getId());
281     loadProfile(identity);
282 
283     return identity;
284 
285   }
286 
287   private List<Identity> getIdentitiesRelationsByFilter(final List<Identity> relations, final ProfileFilter filter,
288                                                         final long offset, final long limit) {
289     
290     if (relations.isEmpty()) return new ArrayList<Identity>();
291     
292     //
293     List<Identity> found = new ArrayList<Identity>();
294     if(relations.isEmpty()) return found ;
295     QueryBuilder<ProfileEntity> builder = getSession().createQueryBuilder(ProfileEntity.class);
296     WhereExpression whereExpression = new WhereExpression();
297     StorageUtils.applyWhereFromIdentity(whereExpression, relations);
298 
299     //
300     StorageUtils.applyFilter(whereExpression, filter);
301 
302     //
303     builder.where(whereExpression.toString()).orderBy(ProfileEntity.fullName.getName(), Ordering.ASC);
304     
305     QueryImpl<ProfileEntity> queryImpl = (QueryImpl<ProfileEntity>) builder.get();
306     ((org.exoplatform.services.jcr.impl.core.query.QueryImpl) queryImpl.getNativeQuery()).setCaseInsensitiveOrder(true);
307     
308     QueryResult<ProfileEntity> result = queryImpl.objects(offset, limit);
309     
310     while(result.hasNext()) {
311       IdentityEntity current = result.next().getIdentity();
312       if (_getMixin(current, DisabledEntity.class, false) != null) {
313         continue;
314       }
315       Identity i = new Identity(current.getProviderId(), current.getRemoteId());
316       i.setId(current.getId());
317       found.add(i);
318     }
319 
320     //
321     return found;
322 
323   }
324 
325   private int getIdentitiesRelationsByFilterCount(final List<Identity> relations, final ProfileFilter filter) {
326 
327     if (relations.size() == 0) {
328       return 0;
329     }
330 
331     //
332     QueryBuilder<ProfileEntity> builder = getSession().createQueryBuilder(ProfileEntity.class);
333 
334     //
335     WhereExpression whereExpression = new WhereExpression();
336     StorageUtils.applyWhereFromIdentity(whereExpression, relations);
337 
338     //
339     StorageUtils.applyFilter(whereExpression, filter);
340     //
341     QueryResult<ProfileEntity> result = builder.where(whereExpression.toString()).get().objects();
342     int number = 0;
343     while (result.hasNext()) {
344       IdentityEntity current = result.next().getIdentity();
345       if (_getMixin(current, DisabledEntity.class, false) == null) {
346         ++number;
347       }
348     }
349     //
350     return number;
351   }
352 
353   private RelationshipStorage getStorage() {
354     return (relationshipStorage != null ? relationshipStorage : this);
355   }
356 
357   /*
358    * Internal
359    */
360 
361   protected RelationshipEntity _createRelationship(final Relationship relationship) throws NodeNotFoundException {
362     String identityId1 = relationship.getSender().getId();
363     String identityId2 = relationship.getReceiver().getId();
364 
365     IdentityEntity identity1 = _findById(IdentityEntity.class, identityId1);
366     IdentityEntity identity2 = _findById(IdentityEntity.class, identityId2);
367 
368     RelationshipEntity createdRelationship = identity1.createRelationship();
369     RelationshipEntity symmetricalRelationship = identity2.createRelationship();
370     
371     switch (relationship.getStatus()) {
372 
373       case PENDING:
374         identity1.getSender().getRelationships().put(identity2.getRemoteId(), createdRelationship);
375         identity2.getReceiver().getRelationships().put(identity1.getRemoteId(), symmetricalRelationship);
376         
377         createdRelationship.setFrom(identity1);
378         createdRelationship.setTo(identity2);
379         
380         symmetricalRelationship.setFrom(identity1);
381         symmetricalRelationship.setTo(identity2);
382         
383         break;
384 
385       case CONFIRMED:
386         identity1.getRelationship().getRelationships().put(identity2.getRemoteId(), createdRelationship);
387         identity2.getRelationship().getRelationships().put(identity1.getRemoteId(), symmetricalRelationship);
388         
389         createdRelationship.setFrom(identity1);
390         createdRelationship.setTo(identity2);
391         
392         symmetricalRelationship.setFrom(identity2);
393         symmetricalRelationship.setTo(identity1);
394         
395         break;
396 
397       case IGNORED:
398         identity1.getIgnore().getRelationships().put(identity2.getRemoteId(), createdRelationship);
399         identity2.getIgnored().getRelationships().put(identity1.getRemoteId(), symmetricalRelationship);
400         
401         createdRelationship.setFrom(identity1);
402         createdRelationship.setTo(identity2);
403         
404         symmetricalRelationship.setFrom(identity1);
405         symmetricalRelationship.setTo(identity2);
406         
407         break;
408 
409     }
410 
411     long createdTimeStamp = System.currentTimeMillis();
412     createdRelationship.setReciprocal(symmetricalRelationship);
413     createdRelationship.setStatus(relationship.getStatus().toString());
414     createdRelationship.setCreatedTime(createdTimeStamp);
415     
416     symmetricalRelationship.setReciprocal(createdRelationship);
417     symmetricalRelationship.setStatus(relationship.getStatus().toString());
418     symmetricalRelationship.setCreatedTime(createdTimeStamp);
419 
420     relationship.setId(createdRelationship.getId());
421 
422     getSession().save();
423 
424     //
425     LOG.debug(String.format(
426         "Relationship from %s:%s to %s:%s created (%s)",
427         createdRelationship.getFrom().getProviderId(),
428         createdRelationship.getFrom().getRemoteId(),
429         createdRelationship.getTo().getProviderId(),
430         createdRelationship.getTo().getRemoteId(),
431         createdRelationship.getPath()
432     ));
433 
434     //
435     LOG.debug(String.format(
436         "Symmetrical relationship from %s:%s to %s:%s created (%s)",
437         symmetricalRelationship.getFrom().getProviderId(),
438         symmetricalRelationship.getFrom().getRemoteId(),
439         symmetricalRelationship.getTo().getProviderId(),
440         symmetricalRelationship.getTo().getRemoteId(),
441         symmetricalRelationship.getPath()
442     ));
443 
444     return createdRelationship;
445   }
446 
447   protected RelationshipEntity _saveRelationship(final Relationship relationship) throws NodeNotFoundException {
448 
449     RelationshipEntity savedRelationship = _findById(RelationshipEntity.class, relationship.getId());
450     RelationshipEntity symmetricalRelationship = savedRelationship.getReciprocal();
451     
452     IdentityEntity sender = _findById(IdentityEntity.class, relationship.getSender().getId());
453     IdentityEntity receiver = _findById(IdentityEntity.class, relationship.getReceiver().getId());
454 
455     savedRelationship.setStatus(relationship.getStatus().toString());
456     symmetricalRelationship.setStatus(relationship.getStatus().toString());
457 
458     switch (relationship.getStatus()) {
459       case PENDING:
460 
461         // Move to sender / receiver
462         savedRelationship.getParent().getParent().getSender().getRelationships()
463             .put(savedRelationship.getName(), savedRelationship);
464 
465         symmetricalRelationship.getParent().getParent().getReceiver().getRelationships()
466             .put(symmetricalRelationship.getName(), symmetricalRelationship);
467         
468         break;
469       case CONFIRMED:
470         
471         //measure the relationship is two ways when relationship is confirmed
472         savedRelationship.setFrom(sender);
473         savedRelationship.setTo(receiver);
474         
475         symmetricalRelationship.setFrom(receiver);
476         symmetricalRelationship.setTo(sender);
477 
478         // Move to relationship
479         savedRelationship.getParent().getParent().getRelationship().getRelationships()
480             .put(savedRelationship.getName(), savedRelationship);
481 
482         symmetricalRelationship.getParent().getParent().getRelationship().getRelationships()
483             .put(symmetricalRelationship.getName(), symmetricalRelationship);
484         
485         updateRelationshipStatistic(sender, true);
486         updateRelationshipStatistic(receiver, true);
487         
488         StreamInvocationHelper.connect(relationship.getSender(), relationship.getReceiver());
489         
490         break;
491       
492       // TODO : IGNORED
493     }
494 
495     //getSession().save();
496 
497     //
498     LOG.debug(String.format(
499         "Relationship from %s:%s to %s:%s saved (%s)",
500         savedRelationship.getFrom().getProviderId(),
501         savedRelationship.getFrom().getRemoteId(),
502         savedRelationship.getTo().getProviderId(),
503         savedRelationship.getTo().getRemoteId(),
504         savedRelationship.getPath()
505     ));
506 
507     //
508     LOG.debug(String.format(
509         "Symmetrical relationship from %s:%s to %s:%s saved (%s)",
510         symmetricalRelationship.getFrom().getProviderId(),
511         symmetricalRelationship.getFrom().getRemoteId(),
512         symmetricalRelationship.getTo().getProviderId(),
513         symmetricalRelationship.getTo().getRemoteId(),
514         symmetricalRelationship.getPath()
515     ));
516 
517     return savedRelationship;
518   }
519   
520   /**
521    * Updates the relationship statistic for the given user. 
522    * 
523    * @param identityEntity the identity
524    * @param isIncreaseCount determines the increase or decrease
525    * @throws NodeNotFoundException
526    */
527   private void updateRelationshipStatistic(IdentityEntity identityEntity, boolean isIncreaseCount) {
528     int newValue = 0;
529     if (identityEntity.hasProperty(IdentityEntity.RELATIONSHIP_NUMBER_PARAM)) {
530       String value = identityEntity.getProperties().get(IdentityEntity.RELATIONSHIP_NUMBER_PARAM);
531       newValue = Integer.valueOf(value);
532       if (isIncreaseCount) {
533         newValue++;
534       } else {
535         newValue--;
536       }
537     } else {
538       if (isIncreaseCount) {
539         newValue = 1;
540       }
541     }
542     
543     identityEntity.setProperty(IdentityEntity.RELATIONSHIP_NUMBER_PARAM, String.valueOf(newValue));
544 
545     
546   }
547 
548   protected List<Relationship> _getSenderRelationships(
549       final Identity sender, final Relationship.Type type, final List<Identity> listCheckIdentity)
550       throws NodeNotFoundException {
551 
552     // TODO : listCheckIdentity ?
553 
554     List<Relationship> relationships = new ArrayList<Relationship>();
555 
556     //
557     IdentityEntity senderEntity = _findById(IdentityEntity.class, sender.getId());
558 
559     if (type == null) {
560       putRelationshipToList(relationships, senderEntity.getRelationship());
561       putRelationshipToList(relationships, senderEntity.getSender());
562     }
563     else {
564       switch (type) {
565 
566         case CONFIRMED:
567           putRelationshipToList(relationships, senderEntity.getRelationship());
568           break;
569 
570         case PENDING:
571           putRelationshipToList(relationships, senderEntity.getSender());
572           break;
573 
574         // TODO : IGNORED
575 
576       }
577     }
578 
579     return relationships;
580   }
581 
582   protected List<Relationship> _getReceiverRelationships(
583       final Identity receiver, final Relationship.Type type, final List<Identity> listCheckIdentity)
584       throws NodeNotFoundException {
585 
586     List<Relationship> relationships = new ArrayList<Relationship>();
587 
588     //
589     IdentityEntity receiverEntity = _findById(IdentityEntity.class, receiver.getId());
590 
591     if (type == null) {
592       putRelationshipToList(relationships, receiverEntity.getRelationship());
593       putRelationshipToList(relationships, receiverEntity.getReceiver());
594     }
595     else {
596       switch (type) {
597 
598         case CONFIRMED:
599           putRelationshipToList(relationships, receiverEntity.getRelationship());
600           break;
601 
602         case PENDING:
603           //SOC-4283 : to work around the problem of wrong data with receiver relationship (sender and receiver value are exchanged)
604           //so we need a specific method to treat the problem
605           putReceiverRelationshipToList(relationships, receiverEntity.getReceiver(), receiver);
606           break;
607 
608         // TODO : IGNORED
609 
610       }
611     }
612 
613     return relationships;
614   }
615 
616   protected Relationship _getRelationship(String uuid) throws NodeNotFoundException {
617 
618     RelationshipEntity relationshipEntity = _findById(RelationshipEntity.class, uuid);
619 
620     IdentityEntity receiverEntity = relationshipEntity.getTo();
621     IdentityEntity senderEntity = relationshipEntity.getFrom();
622 
623     Identity receiver = identityStorage.findIdentityById(receiverEntity.getId());
624     Identity sender = identityStorage.findIdentityById(senderEntity.getId());
625 
626     Relationship relationship = new Relationship(uuid);
627     if (relationshipEntity.isReceiver()) {
628       relationship.setSender(receiver);
629       relationship.setReceiver(sender);
630     }
631     else {
632       relationship.setSender(sender);
633       relationship.setReceiver(receiver);
634     }
635 
636     if (SENDER.equals(relationshipEntity.getParent().getName()) ||
637         RECEIVER.equals(relationshipEntity.getParent().getName())) {
638       relationship.setStatus(Relationship.Type.PENDING);
639     }
640     else {
641       relationship.setStatus(Relationship.Type.CONFIRMED);
642     }
643 
644     // TODO : IGNORED
645 
646     return relationship;
647   }
648 
649   protected Relationship _getRelationship(final Identity identity1, final Identity identity2)
650       throws RelationshipStorageException, NodeNotFoundException {
651     IdentityEntity identityEntity1 = _findById(IdentityEntity.class, identity1.getId());
652     IdentityEntity identityEntity2 = _findById(IdentityEntity.class, identity2.getId());
653 
654     // CONFIRMED
655     RelationshipEntity got = identityEntity1.getRelationship().getRelationships().get(identityEntity2.getName());    
656 
657     // PENDING
658     if (got == null) {
659       got = identityEntity1.getSender().getRelationships().get(identity2.getRemoteId());
660     }
661     if (got == null) {
662       got = identityEntity2.getSender().getRelationships().get(identity1.getRemoteId());
663     }
664 
665     // IGNORED
666     if (got == null) {
667       got = identityEntity1.getIgnore().getRelationships().get(identity2.getRemoteId());
668     }
669     if (got == null) {
670       got = identityEntity2.getIgnore().getRelationships().get(identity1.getRemoteId());
671     }
672     
673     // NOT FOUND
674     if (got == null) {
675       throw new NodeNotFoundException();
676     }
677 
678     Relationship relationship = new Relationship(got.getId());
679 
680     //
681     IdentityEntity senderEntity = got.getFrom();
682     IdentityEntity receiverEntity = got.getTo();
683 
684     Identity sender = new Identity(senderEntity.getId());
685     sender.setRemoteId(senderEntity.getRemoteId());
686     sender.setProviderId(senderEntity.getProviderId());
687 
688     Identity receiver = new Identity(receiverEntity.getId());
689     receiver.setRemoteId(receiverEntity.getRemoteId());
690     receiver.setProviderId(receiverEntity.getProviderId());
691 
692     relationship.setSender(sender);
693     relationship.setReceiver(receiver);
694 
695     relationship.setStatus(Relationship.Type.valueOf(got.getStatus()));
696 
697     return relationship;
698   }
699 
700   /*
701    * Public
702    */
703 
704   /**
705    * {@inheritDoc}
706    */
707   public Relationship saveRelationship(final Relationship relationship) throws RelationshipStorageException {
708     try {
709       if (relationship.getId() == null) {
710         _createRelationship(relationship);
711       }
712       else {
713         _saveRelationship(relationship);
714         //
715         StorageUtils.persist();
716       }
717     }
718     catch (NodeNotFoundException e) {
719       throw new RelationshipStorageException(
720           RelationshipStorageException.Type.ILLEGAL_ARGUMENTS,
721           new String[] { Relationship.class.getSimpleName() });
722     }
723     //
724     clearActivityStorageCache();
725 
726     return relationship;
727   }
728 
729   /**
730    * {@inheritDoc}
731    */
732   public void removeRelationship(Relationship relationship) throws RelationshipStorageException {
733 
734     try {
735       RelationshipEntity toDeleteRelationship = _findById(RelationshipEntity.class, relationship.getId());
736       RelationshipEntity symmetricalRelationship = toDeleteRelationship.getReciprocal();
737 
738       IdentityEntity from = toDeleteRelationship.getFrom();
739       IdentityEntity to = toDeleteRelationship.getTo();
740       
741       if(Relationship.Type.CONFIRMED.equals(relationship.getStatus())) {
742         updateRelationshipStatistic(from, false);
743         updateRelationshipStatistic(to, false);
744       }
745 
746       _removeById(RelationshipEntity.class, symmetricalRelationship.getId());
747       _removeById(RelationshipEntity.class, relationship.getId());
748       
749       StorageUtils.persist();
750       
751       StreamInvocationHelper.deleteConnect(relationship.getSender(), relationship.getReceiver());
752       
753       clearActivityStorageCache();
754 
755       //
756       LOG.debug(String.format(
757           "Symmetrical relationship from %s:%s to %s:%s removed",
758           to.getProviderId(),
759           to.getRemoteId(),
760           from.getProviderId(),
761           from.getRemoteId()
762       ));
763 
764       //
765       LOG.debug(String.format(
766           "Relationship from %s:%s to %s:%s removed",
767           from.getProviderId(),
768           from.getRemoteId(),
769           to.getProviderId(),
770           to.getRemoteId()
771       ));
772     }
773     catch (NodeNotFoundException e) {
774       throw new RelationshipStorageException(RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP);
775     }
776   }
777 
778   /**
779    * {@inheritDoc}
780    */
781   public Relationship getRelationship(String uuid) throws RelationshipStorageException {
782 
783     try {
784       return _getRelationship(uuid);
785     }
786     catch (NodeNotFoundException e) {
787       return null;
788     }
789   }
790 
791   /**
792    * {@inheritDoc}
793    */
794   public List<Relationship> getSenderRelationships(
795       final Identity sender, final Relationship.Type type, final List<Identity> listCheckIdentity)
796       throws RelationshipStorageException {
797 
798     try {
799       return _getSenderRelationships(sender, type, listCheckIdentity);
800     }
801     catch (NodeNotFoundException e) {
802       throw new RelationshipStorageException(
803           RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP, null, e, sender.getId(), type.toString());
804     }
805   }
806 
807   /**
808    * {@inheritDoc}
809    */
810   public List<Relationship> getSenderRelationships(
811       final String senderId, final Relationship.Type type, final List<Identity> listCheckIdentity)
812       throws RelationshipStorageException {
813 
814     return getSenderRelationships(new Identity(senderId), type, listCheckIdentity);
815 
816   }
817 
818   /**
819    * {@inheritDoc}
820    */
821   public List<Relationship> getReceiverRelationships(
822       final Identity receiver, final Relationship.Type type, final List<Identity> listCheckIdentity)
823       throws RelationshipStorageException {
824 
825     try {
826       return _getReceiverRelationships(receiver, type, listCheckIdentity);
827     }
828     catch (NodeNotFoundException e) {
829       throw new RelationshipStorageException(
830           RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP, null, e, receiver.getId(), type.toString());
831     }
832   }
833 
834   /**
835    * {@inheritDoc}
836    */
837   public Relationship getRelationship(final Identity identity1, final Identity identity2)
838       throws RelationshipStorageException {
839 
840     try {
841       return _getRelationship(identity1, identity2);
842     }
843     catch (NodeNotFoundException e) {
844       return null;
845     }
846   }
847   
848   @Override
849   public boolean hasRelationship(Identity identity1, Identity identity2, String relationshipPath) throws RelationshipStorageException {
850     //it implemented on CachedRelationshipStorage
851     throw new RelationshipStorageException(RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP_OF_THEM, "hasRelationship() unsupported!"); 
852   }
853 
854   /**
855    * {@inheritDoc}
856    */
857   public List<Relationship> getRelationships(
858       final Identity identity, final Relationship.Type type, final List<Identity> listCheckIdentity)
859       throws RelationshipStorageException {
860     
861     try {
862       List<Relationship> relationships = new ArrayList<Relationship>();
863 
864       //
865       IdentityEntity receiverEntity = _findById(IdentityEntity.class, identity.getId());
866 
867       if (type == null) {
868         putRelationshipToList(relationships, receiverEntity.getRelationship());
869         putRelationshipToList(relationships, receiverEntity.getReceiver());
870         putRelationshipToList(relationships, receiverEntity.getSender());
871       }
872       else {
873         switch (type) {
874 
875           case CONFIRMED:
876             putRelationshipToList(relationships, receiverEntity.getRelationship());
877             break;
878 
879           case PENDING:
880             putRelationshipToList(relationships, receiverEntity.getReceiver());
881             putRelationshipToList(relationships, receiverEntity.getSender());
882             break;
883 
884           case IGNORED:
885             putRelationshipToList(relationships, receiverEntity.getIgnored());
886             break;
887 
888         }
889       }
890       
891       return relationships;
892     }
893     catch (NodeNotFoundException e) {
894       return new ArrayList<Relationship>();
895     }
896   }
897   
898   /**
899    * {@inheritDoc}
900    */
901   public List<Identity> getLastConnections(Identity identity, int limit) throws RelationshipStorageException {
902     //check the limit parameter
903     if (limit <= 0) {
904       return new ArrayList<Identity>();
905     }
906     //
907     List<Identity> identities = new ArrayList<Identity>();
908     try {
909       IdentityEntity identityEntity = _findById(IdentityEntity.class, identity.getId());
910       String relationshipNodePath = identityEntity.getPath() + StorageUtils.SLASH_STR + StorageUtils.SOC_RELATIONSHIP;
911       Node node = (Node) getSession().getJCRSession().getItem(relationshipNodePath);
912       NodeIterator iterator = AbstractService.getNodeIteratorOrderDESC(node);
913       while (iterator.hasNext() && limit > 0) {
914         Node relNode = iterator.nextNode();
915         if (relNode.getName().contains(StorageUtils.COLON_STR)) {
916           String remoteId = relNode.getName().split(StorageUtils.COLON_STR)[1];
917           Identity newIdentity = identityStorage.findIdentity(OrganizationIdentityProvider.NAME, remoteId);
918           if (newIdentity != null) {//SOC-4865 : avoid the case that user has been deleted but the relationship is kept
919             identities.add(newIdentity);
920             limit--;
921           }
922         }
923       }
924     }
925     catch (Exception e) {
926       throw new RelationshipStorageException(
927            RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP,
928            e.getMessage());
929     }
930 
931     return identities;
932   }
933 
934   /**
935    * {@inheritDoc}
936    */
937   public List<Identity> getRelationships(final Identity identity, long offset, long limit)
938       throws RelationshipStorageException {
939 
940     List<Identity> identities = new ArrayList<Identity>();
941 
942     try {
943 
944       IdentityEntity identityEntity = _findById(IdentityEntity.class, identity.getId());
945 
946       QueryBuilder<RelationshipEntity> builder = getSession().createQueryBuilder(RelationshipEntity.class);
947 
948       WhereExpression whereExpression = new WhereExpression();
949       whereExpression.like(JCRProperties.path, identityEntity.getPath() + StorageUtils.SLASH_STR + StorageUtils.PERCENT_STR);
950 
951       builder.where(whereExpression.toString());
952       builder.orderBy(RelationshipEntity.createdTime.getName(), Ordering.DESC);
953 
954       QueryResult<RelationshipEntity> results = builder.get().objects(offset, limit);
955 
956       while (results.hasNext()) {
957 
958         RelationshipEntity currentRelationshipEntity = results.next();
959         IdentityEntity gotIdentityEntity;
960         if (currentRelationshipEntity.isReceiver()) {
961           gotIdentityEntity = currentRelationshipEntity.getFrom();
962         }
963         else {
964           gotIdentityEntity = currentRelationshipEntity.getTo();
965         }
966         //
967         if (_getMixin(gotIdentityEntity, DisabledEntity.class, false) != null) {
968           continue;
969         }
970 
971         Identity newIdentity = new Identity(gotIdentityEntity.getId());
972         newIdentity.setProviderId(gotIdentityEntity.getProviderId());
973         newIdentity.setRemoteId(gotIdentityEntity.getRemoteId());
974 
975         identities.add(newIdentity);
976       }
977 
978     }
979     catch (NodeNotFoundException e) {
980       throw new RelationshipStorageException(
981            RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP,
982            e.getMessage());
983     }
984 
985     return identities;
986   }
987 
988   /**
989    * {@inheritDoc}
990    */
991   public List<Identity> getIncomingRelationships(Identity receiver,
992                                                  long offset, long limit) throws RelationshipStorageException {
993 
994     try {
995       
996       IdentityEntity receiverEntity = _findById(IdentityEntity.class, receiver.getId());
997 
998       Iterator<RelationshipEntity> it = receiverEntity.getReceiver().getRelationships().values().iterator();
999       //SOC-4283 : to work around the problem of wrong data with receiver relationship (sender and receiver value are exchanged)
1000       //so we need a specific method to treat the problem
1001       return getIdentitiesFromRelationship(it, receiver, offset, limit);
1002 
1003     }
1004     catch (NodeNotFoundException e) {
1005       throw new RelationshipStorageException(
1006            RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP,
1007            e.getMessage());
1008     }
1009 
1010   }
1011 
1012   /**
1013    * {@inheritDoc}
1014    */
1015   public int getIncomingRelationshipsCount(Identity receiver) throws RelationshipStorageException {
1016     //
1017     return getIncomingRelationships(receiver, 0, -1).size();
1018   }
1019 
1020   /**
1021    * {@inheritDoc}
1022    */
1023   public List<Identity> getOutgoingRelationships(Identity sender,
1024                                                  long offset, long limit) throws RelationshipStorageException {
1025 
1026     try {
1027 
1028       IdentityEntity senderEntity = _findById(IdentityEntity.class, sender.getId());
1029 
1030       Iterator<RelationshipEntity> it = senderEntity.getSender().getRelationships().values().iterator();
1031       return getIdentitiesFromRelationship(it, Origin.TO, offset, limit);
1032 
1033     }
1034     catch (NodeNotFoundException e) {
1035       throw new RelationshipStorageException(
1036            RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP,
1037            e.getMessage());
1038     }
1039 
1040   }
1041 
1042   /**
1043    * {@inheritDoc}
1044    */
1045   public int getOutgoingRelationshipsCount(Identity sender) throws RelationshipStorageException {
1046     return getOutgoingRelationships(sender, 0, -1).size();
1047   }
1048 
1049   /**
1050    * {@inheritDoc}
1051    */
1052    public int getRelationshipsCount(Identity identity) throws RelationshipStorageException {
1053 
1054      int nb = 0;
1055 
1056      //
1057      try {
1058 
1059        IdentityEntity identityEntity = _findById(IdentityEntity.class, identity.getId());
1060        nb += identityEntity.getRelationship().getRelationships().size();
1061        nb += identityEntity.getSender().getRelationships().size();
1062        nb += identityEntity.getReceiver().getRelationships().size();
1063        nb += identityEntity.getIgnore().getRelationships().size();
1064 
1065        return nb;
1066        
1067      }
1068      catch (NodeNotFoundException e) {
1069 
1070        throw new RelationshipStorageException(
1071            RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP,
1072            e.getMessage());
1073 
1074      }
1075    }
1076 
1077   /**
1078    * {@inheritDoc}
1079    */
1080   public List<Identity> getConnections(Identity identity, long offset, long limit) throws RelationshipStorageException {
1081 
1082     try {
1083       IdentityEntity identityEntity = _findById(IdentityEntity.class, identity.getId());
1084 
1085       Iterator<RelationshipEntity> it = identityEntity.getRelationship().getRelationships().values().iterator();
1086       return getIdentitiesFromRelationship(it, Origin.TO, offset, limit);
1087 
1088     }
1089     catch (NodeNotFoundException e) {
1090       throw new RelationshipStorageException(RelationshipStorageException.Type.ILLEGAL_ARGUMENTS);
1091     }
1092   }
1093 
1094   /**
1095    * {@inheritDoc}
1096    */
1097   public List<Identity> getConnections(Identity identity) throws RelationshipStorageException {
1098 
1099     return getConnections(identity, 0, -1);
1100 
1101   }
1102 
1103   /**
1104    * {@inheritDoc}
1105    */
1106   public int getConnectionsCount(Identity identity) throws RelationshipStorageException {
1107     try {
1108       // TODO : use property to improve the perfs
1109       IdentityEntity identityEntity = _findById(IdentityEntity.class, identity.getId());
1110       if (identityEntity.hasProperty(IdentityEntity.RELATIONSHIP_NUMBER_PARAM)) {
1111         String value = identityEntity.getProperties().get(IdentityEntity.RELATIONSHIP_NUMBER_PARAM);
1112         return Integer.valueOf(value);
1113       } else {
1114         //
1115         int totalSize = identityEntity.getRelationship().getRelationships().size();
1116         identityEntity.setProperty(IdentityEntity.RELATIONSHIP_NUMBER_PARAM, String.valueOf(totalSize));
1117         getSession().save();
1118         return totalSize;
1119       }
1120     }
1121     catch (NodeNotFoundException e) {
1122       throw new RelationshipStorageException(RelationshipStorageException.Type.ILLEGAL_ARGUMENTS);
1123     }
1124   }
1125 
1126   /**
1127    * {@inheritDoc}
1128    */
1129   public List<Identity> getConnectionsByFilter(
1130       final Identity existingIdentity, final ProfileFilter profileFilter, final long offset, final long limit)
1131       throws RelationshipStorageException {
1132 
1133     List<Identity> identities = getStorage().getConnections(existingIdentity);
1134     return getIdentitiesRelationsByFilter(identities, profileFilter, offset, limit);
1135     
1136   }
1137 
1138   /**
1139    * {@inheritDoc}
1140    */
1141   public List<Identity> getIncomingByFilter(
1142       final Identity existingIdentity, final ProfileFilter profileFilter, final long offset, final long limit)
1143       throws RelationshipStorageException {
1144     //
1145     if (profileFilter.isEmpty()) {
1146       return StorageUtils.sortIdentitiesByFullName(getIncomingRelationships(existingIdentity, offset, limit), true);
1147     }
1148 
1149     List<Identity> identities = getStorage().getIncomingRelationships(existingIdentity, 0, -1);
1150     return getIdentitiesRelationsByFilter(identities, profileFilter, offset, limit);
1151 
1152   }
1153   
1154   /**
1155    * {@inheritDoc}
1156    */
1157   public List<Identity> getOutgoingByFilter(
1158       final Identity existingIdentity, final ProfileFilter profileFilter, final long offset, final long limit)
1159       throws RelationshipStorageException {
1160 
1161     if (profileFilter.isEmpty()) {
1162       return StorageUtils.sortIdentitiesByFullName(getOutgoingRelationships(existingIdentity, offset, limit), true);
1163     }
1164     
1165     List<Identity> identities = getStorage().getOutgoingRelationships(existingIdentity, 0, -1);
1166     return getIdentitiesRelationsByFilter(identities, profileFilter, offset, limit);
1167 
1168   }
1169   /**
1170    * {@inheritDoc}
1171    */
1172   public int getIncomingCountByFilter(
1173       final Identity existingIdentity, final ProfileFilter profileFilter) throws RelationshipStorageException {
1174     
1175     if (profileFilter.isEmpty()) {
1176       return getIncomingRelationshipsCount(existingIdentity);
1177     }
1178 
1179     List<Identity> identities = getStorage().getIncomingRelationships(existingIdentity, 0, -1);
1180     return getIdentitiesRelationsByFilterCount(identities, profileFilter);
1181 
1182   }
1183   
1184   /**
1185    * {@inheritDoc}
1186    */
1187   public int getConnectionsCountByFilter(
1188       final Identity existingIdentity, final ProfileFilter profileFilter) throws RelationshipStorageException {
1189 
1190     List<Identity> identities = getStorage().getConnections(existingIdentity);
1191     return getIdentitiesRelationsByFilterCount(identities, profileFilter);
1192 
1193   }
1194   
1195   /**
1196    * {@inheritDoc}
1197    */
1198   public int getOutgoingCountByFilter(
1199       final Identity existingIdentity, final ProfileFilter profileFilter) throws RelationshipStorageException {
1200     
1201     if (profileFilter.isEmpty()) {
1202       return getOutgoingRelationshipsCount(existingIdentity);
1203     }
1204 
1205     List<Identity> identities = getStorage().getOutgoingRelationships(existingIdentity, 0, -1);
1206     return getIdentitiesRelationsByFilterCount(identities, profileFilter);
1207 
1208   }
1209 
1210   /**
1211    * {@inheritDoc}
1212    */
1213   public Map<Identity, Integer> getSuggestions(Identity currentIdentity, int maxConnections, 
1214                                                 int maxConnectionsToLoad, 
1215                                                 int maxSuggestions) throws RelationshipStorageException {
1216     try {
1217       return _getSuggestions(currentIdentity, maxConnections, maxConnectionsToLoad, maxSuggestions);
1218     } catch (Exception e) {
1219       throw new RelationshipStorageException(RelationshipStorageException.Type.FAILED_TO_GET_SUGGESTION, e);
1220     }
1221   }
1222 
1223   public Map<Identity, Integer> _getSuggestions(Identity currentIdentity, int maxConnections, 
1224                                                 int maxConnectionsToLoad, 
1225                                                 int maxSuggestions) throws Exception {
1226     if (maxConnectionsToLoad > 0 && maxConnections > maxConnectionsToLoad)
1227        maxConnectionsToLoad = maxConnections;
1228      // Get identities level 1
1229     Set<Identity> relationIdLevel1 = new HashSet<Identity>();
1230     RelationshipManager relationshipManager = getRelationshipManager();
1231     ListAccess<Identity> allConnections = relationshipManager.getConnections(currentIdentity);
1232     int size = allConnections.getSize();
1233     // The ideal limit of connection to treat however we could need to go beyond this limit
1234     // if we cannot reach the expected amount of suggestions
1235     int endIndex;
1236     Random random = new Random();
1237     Identity[] connections;
1238     if (size > maxConnectionsToLoad && maxConnectionsToLoad > 0 && maxConnections > 0) {
1239       // The total amount of connections is bigger than the maximum allowed
1240       // We will then load only a random sample to reduce the best we can the 
1241       // required time for this task 
1242       int startIndex = random.nextInt(size - maxConnectionsToLoad);
1243       endIndex = maxConnections;
1244       connections= allConnections.load(startIndex, maxConnectionsToLoad);
1245     } else {
1246       // The total amount of connections is less than the maximum allowed
1247       // We call load everything
1248       endIndex = size;
1249       connections= allConnections.load(0, size);
1250     }
1251     // we need to load all the connections
1252     for (int i = 0; i < connections.length; i++) {
1253       Identity id = connections[i];
1254       relationIdLevel1.add(id);
1255     }
1256     relationIdLevel1.remove(currentIdentity);
1257 
1258     // Get identities level 2 (suggested Identities)
1259     Map<Identity, Integer> suggestedIdentities = new HashMap<Identity, Integer>();
1260     Iterator<Identity> it = relationIdLevel1.iterator();
1261     for (int j = 0; j < size && it.hasNext(); j++) {
1262       Identity id = it.next();
1263       // We check if we reach the limit of connections to treat and if we have enough suggestions
1264       if (j >= endIndex && suggestedIdentities.size() > maxSuggestions && maxSuggestions > 0)
1265         break;
1266       ListAccess<Identity> allConns = relationshipManager.getConnections(id);
1267       int allConnSize = allConns.getSize();
1268       int allConnStartIndex = 0;
1269       if (allConnSize > maxConnections && maxConnections > 0) {
1270         // The current identity has more connections that the allowed amount so we will treat a sample
1271         allConnStartIndex = random.nextInt(allConnSize - maxConnections);
1272         connections = allConns.load(allConnStartIndex, maxConnections);
1273       } else {
1274         // The current identity doesn't have more connections that the allowed amount so we will 
1275         // treat all of them
1276         connections = allConns.load(0, allConnSize);
1277       }
1278       for (int i = 0; i < connections.length; i++) {
1279         Identity ids = connections[i];
1280         // We check if the current connection is not already part of the connections of the identity
1281         // for which we seek some suggestions
1282         if (!relationIdLevel1.contains(ids) && !ids.equals(currentIdentity) && !ids.isDeleted()
1283              && relationshipManager.get(ids, currentIdentity) == null) {
1284           Integer commonIdentities = suggestedIdentities.get(ids);
1285           if (commonIdentities == null) {
1286             commonIdentities = new Integer(1);
1287           } else {
1288             commonIdentities = new Integer(commonIdentities.intValue() + 1);
1289           }
1290           suggestedIdentities.put(ids, commonIdentities);
1291         }
1292       }
1293     }
1294     NavigableMap<Integer, List<Identity>> groupByCommonConnections = new TreeMap<Integer, List<Identity>>();
1295     // This for loop allows to group the suggestions by total amount of common connections
1296     for (Identity identity : suggestedIdentities.keySet()) {
1297       Integer commonIdentities = suggestedIdentities.get(identity);
1298       List<Identity> ids = groupByCommonConnections.get(commonIdentities);
1299       if (ids == null) {
1300         ids = new ArrayList<Identity>();
1301         groupByCommonConnections.put(commonIdentities, ids);
1302       }
1303       ids.add(identity);
1304     }
1305     Map<Identity, Integer> suggestions = new LinkedHashMap<Identity, Integer>();
1306     int suggestionLeft = maxSuggestions;
1307     // We iterate over the suggestions starting from the suggestions with the highest amount of common
1308     // connections
1309     main: for (Integer key : groupByCommonConnections.descendingKeySet()) {
1310       List<Identity> ids = groupByCommonConnections.get(key);
1311       for (Identity identity : ids) {
1312         suggestions.put(identity, key);
1313         // We stop once we have enough suggestions
1314         if (maxSuggestions > 0 && --suggestionLeft == 0)
1315           break main;
1316       }
1317     }
1318     return suggestions;
1319   }
1320 
1321   public void setStorage(RelationshipStorage storage) {
1322     this.relationshipStorage = storage;
1323   }
1324   
1325   /**
1326    * {@inheritDoc}
1327    */
1328   public List<Relationship> getRelationshipsByStatus(Identity identity, Relationship.Type type, long offset, long limit) {
1329 
1330     try {
1331       Session jcrSession = getSession().getJCRSession();
1332       List<Relationship> relationships = new ArrayList<Relationship>();
1333 
1334       //
1335       IdentityEntity receiverEntity = _findById(IdentityEntity.class, identity.getId());
1336       String relationshipNodePath = receiverEntity.getPath() + StorageUtils.SLASH_STR + StorageUtils.SOC_RELATIONSHIP;;
1337       String receiverNodePath = receiverEntity.getPath() + StorageUtils.SLASH_STR + StorageUtils.SOC_RELCEIVER;
1338       String senderNodePath = receiverEntity.getPath() + StorageUtils.SLASH_STR + StorageUtils.SOC_SENDER;
1339       Node node = null;
1340       NodeIterator relationIterator = null;
1341       NodeIterator senderIterator = null;
1342       NodeIterator receiverIterator = null;
1343       
1344       switch (type) {
1345 
1346       case ALL:
1347         node = (Node) jcrSession.getItem(relationshipNodePath);
1348         relationIterator = AbstractService.getNodeIteratorOrderDESC(node);
1349         node = (Node) jcrSession.getItem(receiverNodePath);
1350         senderIterator = AbstractService.getNodeIteratorOrderDESC(node);
1351         node = (Node) jcrSession.getItem(senderNodePath);
1352         receiverIterator = AbstractService.getNodeIteratorOrderDESC(node);
1353         getRelationships(relationships, new IteratorIterator<Node>(relationIterator, senderIterator, receiverIterator), offset, limit);
1354         break;
1355 
1356       case CONFIRMED:
1357         node = (Node) jcrSession.getItem(relationshipNodePath);
1358         relationIterator = AbstractService.getNodeIteratorOrderDESC(node);
1359         
1360         getRelationships(relationships, new IteratorIterator<Node>(relationIterator), offset, limit);
1361         break;
1362 
1363       case PENDING:
1364         node = (Node) jcrSession.getItem(receiverNodePath);
1365         receiverIterator = AbstractService.getNodeIteratorOrderDESC(node);
1366         node = (Node) jcrSession.getItem(senderNodePath);
1367         senderIterator = AbstractService.getNodeIteratorOrderDESC(node);
1368         getRelationships(relationships, new IteratorIterator<Node>(receiverIterator, senderIterator), offset, limit);
1369         break;
1370 
1371       case IGNORED:
1372         break;
1373       }
1374 
1375       return relationships;
1376     } catch (NodeNotFoundException e) {
1377       return new ArrayList<Relationship>();
1378     } catch (Exception e) {
1379       throw new RelationshipStorageException(RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP,
1380                                              e.getMessage());
1381     }
1382   }
1383   
1384   /**
1385    * {@inheritDoc}
1386    */
1387   public int getRelationshipsCountByStatus(Identity identity, Relationship.Type type) {
1388     try {
1389       IdentityEntity receiverEntity = _findById(IdentityEntity.class, identity.getId());
1390 
1391       switch (type) {
1392 
1393       case ALL:
1394         return receiverEntity.getRelationship().getRelationships().size()
1395             + receiverEntity.getReceiver().getRelationships().size()
1396             + receiverEntity.getSender().getRelationships().size();
1397       case CONFIRMED:
1398         return receiverEntity.getRelationship().getRelationships().size();
1399       case PENDING:
1400         return receiverEntity.getReceiver().getRelationships().size()
1401             + receiverEntity.getSender().getRelationships().size();
1402       case IGNORED:
1403         return receiverEntity.getIgnored().getRelationships().size();
1404       }
1405     } catch (Exception e) {
1406       return 0;
1407     }
1408     
1409     return 0;
1410   }
1411   
1412   private void getRelationships(List<Relationship> relationships, IteratorIterator<Node> nodeIt, long offset, long limit) {
1413     //
1414     int i = 0;
1415 
1416     _skip(nodeIt, offset);
1417 
1418     Relationship relationship = null;
1419     try {
1420       while (nodeIt.hasNext()) {
1421         Node relationshipNode = (Node) nodeIt.next();
1422         relationship = new Relationship(relationshipNode.getUUID());
1423         IdentityEntity senderEntity = _findById(IdentityEntity.class, relationshipNode.getProperty(StorageUtils.SOC_FROM).getString());
1424         IdentityEntity receiverEntity = _findById(IdentityEntity.class, relationshipNode.getProperty(StorageUtils.SOC_TO).getString());
1425         //
1426         if (_getMixin(senderEntity, DisabledEntity.class, false) != null ||
1427             _getMixin(receiverEntity, DisabledEntity.class, false) != null) {
1428           continue;
1429         }
1430         Identity sender = new Identity(senderEntity.getId());
1431         sender.setRemoteId(senderEntity.getRemoteId());
1432         sender.setProviderId(senderEntity.getProviderId());
1433         ProfileEntity senderProfileEntity = senderEntity.getProfile();
1434   
1435         if (senderProfileEntity != null) {
1436           loadProfile(sender);
1437         }
1438   
1439         Identity receiver = new Identity(receiverEntity.getId());
1440         receiver.setRemoteId(receiverEntity.getRemoteId());
1441         receiver.setProviderId(receiverEntity.getProviderId());
1442         ProfileEntity receiverProfileEntity = receiverEntity.getProfile();
1443   
1444         if (receiverProfileEntity != null) {
1445           loadProfile(receiver);
1446         }
1447   
1448         relationship.setSender(sender);
1449         relationship.setReceiver(receiver);
1450         if (StorageUtils.SOC_SENDER.equals(relationshipNode.getParent().getName()) ||
1451             StorageUtils.SOC_RELCEIVER.equals(relationshipNode.getParent().getName())) {
1452           relationship.setStatus(Relationship.Type.PENDING);
1453         }
1454         else {
1455           relationship.setStatus(Relationship.Type.CONFIRMED);
1456         }
1457   
1458         relationships.add(relationship);
1459         
1460         if (limit != -1 && limit > 0 && ++i >= limit) {
1461           break;
1462         }
1463       }
1464     } catch (Exception e) {
1465       throw new RelationshipStorageException(RelationshipStorageException.Type.FAILED_TO_GET_RELATIONSHIP,
1466                                              e.getMessage());
1467     }
1468   }
1469   
1470   class IteratorIterator<T> implements Iterator<T> {
1471     private final Iterator<T> is[];
1472 
1473     private int               current;
1474 
1475     public IteratorIterator(Iterator<T>... iterators) {
1476       is = iterators;
1477       current = 0;
1478     }
1479 
1480     public boolean hasNext() {
1481       while (current < is.length && !is[current].hasNext())
1482         current++;
1483 
1484       return current < is.length;
1485     }
1486 
1487     public T next() {
1488       while (current < is.length && !is[current].hasNext())
1489         current++;
1490 
1491       return is[current].next();
1492     }
1493 
1494     public void remove() { /* not implemented */
1495     }
1496   }
1497 }