Java tutorial
/* * Copyright (C) 2003-2015 eXo Platform SAS. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.exoplatform.social.addons.storage; import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.StringTokenizer; import javax.persistence.EntityManager; import javax.persistence.Query; import javax.persistence.Tuple; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.exoplatform.commons.api.persistence.ExoTransactional; import org.exoplatform.commons.file.model.FileItem; import org.exoplatform.commons.file.services.FileService; import org.exoplatform.commons.persistence.impl.EntityManagerService; import org.exoplatform.commons.utils.CommonsUtils; import org.exoplatform.commons.utils.ListAccess; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.organization.MembershipTypeHandler; import org.exoplatform.services.organization.OrganizationService; import org.exoplatform.services.organization.User; import org.exoplatform.services.user.UserStateModel; import org.exoplatform.services.user.UserStateService; import org.exoplatform.social.addons.rest.IdentityAvatarRestService; import org.exoplatform.social.addons.search.ExtendProfileFilter; import org.exoplatform.social.addons.search.ProfileSearchConnector; import org.exoplatform.social.addons.storage.dao.ActivityDAO; import org.exoplatform.social.addons.storage.dao.IdentityDAO; import org.exoplatform.social.addons.storage.dao.SpaceDAO; import org.exoplatform.social.addons.storage.entity.ActivityEntity; import org.exoplatform.social.addons.storage.entity.ConnectionEntity; import org.exoplatform.social.addons.storage.entity.IdentityEntity; import org.exoplatform.social.addons.storage.entity.ProfileExperienceEntity; import org.exoplatform.social.addons.storage.entity.SpaceEntity; import org.exoplatform.social.core.identity.SpaceMemberFilterListAccess; import org.exoplatform.social.core.identity.model.ActiveIdentityFilter; import org.exoplatform.social.core.identity.model.Identity; import org.exoplatform.social.core.identity.model.IdentityWithRelationship; import org.exoplatform.social.core.identity.model.Profile; import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider; import org.exoplatform.social.core.identity.provider.SpaceIdentityProvider; import org.exoplatform.social.core.model.AvatarAttachment; import org.exoplatform.social.core.profile.ProfileFilter; import org.exoplatform.social.core.relationship.model.Relationship; import org.exoplatform.social.core.service.LinkProvider; import org.exoplatform.social.core.space.SpaceUtils; import org.exoplatform.social.core.space.model.Space; import org.exoplatform.social.core.storage.IdentityStorageException; import org.exoplatform.social.core.storage.impl.IdentityStorageImpl; import org.exoplatform.social.core.storage.impl.StorageUtils; /** * Created by The eXo Platform SAS * Author : eXoPlatform * exo@exoplatform.com * Oct 5, 2015 */ public class RDBMSIdentityStorageImpl extends IdentityStorageImpl { private static final Log LOG = ExoLogger.getLogger(RDBMSIdentityStorageImpl.class); private static final String socialNameSpace = "social"; private final ActivityDAO activityDAO; private final IdentityDAO identityDAO; private final SpaceDAO spaceDAO; private final FileService fileService; private final OrganizationService orgService; private ProfileSearchConnector profileSearchConnector; public RDBMSIdentityStorageImpl(IdentityDAO identityDAO, SpaceDAO spaceDAO, ActivityDAO activityDAO, FileService fileService, ProfileSearchConnector profileSearchConnector, OrganizationService orgService) { this.identityDAO = identityDAO; this.spaceDAO = spaceDAO; this.activityDAO = activityDAO; this.profileSearchConnector = profileSearchConnector; this.orgService = orgService; this.fileService = fileService; } private IdentityDAO getIdentityDAO() { return identityDAO; } public void setProfileSearchConnector(ProfileSearchConnector profileSearchConnector) { this.profileSearchConnector = profileSearchConnector; } private void mapToProfileEntity(Profile profile, IdentityEntity entity) { Map<String, String> entityProperties = entity.getProperties(); if (entityProperties == null) { entityProperties = new HashMap<>(); } String providerId = profile.getIdentity().getProviderId(); if (!OrganizationIdentityProvider.NAME.equals(providerId) && !SpaceIdentityProvider.NAME.equals(providerId)) { entityProperties.put(Profile.URL, profile.getUrl()); entityProperties.put(Profile.AVATAR_URL, profile.getAvatarUrl()); } Map<String, Object> properties = profile.getProperties(); for (Map.Entry<String, Object> e : properties.entrySet()) { if (Profile.AVATAR.equalsIgnoreCase(e.getKey())) { AvatarAttachment attachment = (AvatarAttachment) e.getValue(); byte[] bytes = attachment.getImageBytes(); String fileName = attachment.getFileName(); if (fileName == null) { fileName = entity.getRemoteId() + "_avatar"; } try { Long avatarId = entity.getAvatarFileId(); FileItem fileItem; if (avatarId != null) {//update avatar file fileItem = new FileItem(avatarId, fileName, attachment.getMimeType(), socialNameSpace, bytes.length, new Date(), entity.getRemoteId(), false, new ByteArrayInputStream(bytes)); fileService.updateFile(fileItem); } else {//create new avatar file fileItem = new FileItem(null, fileName, attachment.getMimeType(), socialNameSpace, bytes.length, new Date(), entity.getRemoteId(), false, new ByteArrayInputStream(bytes)); fileItem = fileService.writeFile(fileItem); entity.setAvatarFileId(fileItem.getFileInfo().getId()); } } catch (Exception ex) { LOG.warn("Can not store avatar for " + entity.getProviderId() + " " + entity.getRemoteId(), ex); } } else if (Profile.EXPERIENCES.equalsIgnoreCase(e.getKey())) { List<Map<String, String>> exps = (List<Map<String, String>>) e.getValue(); Set<ProfileExperienceEntity> experiences = new HashSet<>(); for (Map<String, String> exp : exps) { ProfileExperienceEntity ex = new ProfileExperienceEntity(); ex.setCompany(exp.get(Profile.EXPERIENCES_COMPANY)); ex.setPosition(exp.get(Profile.EXPERIENCES_POSITION)); ex.setStartDate(exp.get(Profile.EXPERIENCES_START_DATE)); ex.setEndDate(exp.get(Profile.EXPERIENCES_END_DATE)); ex.setSkills(exp.get(Profile.EXPERIENCES_SKILLS)); ex.setDescription(exp.get(Profile.EXPERIENCES_DESCRIPTION)); experiences.add(ex); } entity.setExperiences(experiences); } else if (Profile.CONTACT_IMS.equals(e.getKey()) || Profile.CONTACT_PHONES.equals(e.getKey()) || Profile.CONTACT_URLS.equals(e.getKey())) { List<Map<String, String>> list = (List<Map<String, String>>) e.getValue(); JSONArray arr = new JSONArray(); for (Map<String, String> map : list) { JSONObject json = new JSONObject(map); arr.put(json); } entityProperties.put(e.getKey(), arr.toString()); } else if (!Profile.EXPERIENCES_SKILLS.equals(e.getKey())) { Object val = e.getValue(); if (val != null) { entityProperties.put(e.getKey(), String.valueOf(val)); } } } entity.setProperties(entityProperties); Date created = profile.getCreatedTime() <= 0 ? new Date() : new Date(profile.getCreatedTime()); entity.setCreatedDate(created); } /** * Saves identity. * * @param identity the identity * @throws IdentityStorageException if has any error */ public void saveIdentity(final Identity identity) throws IdentityStorageException { long id = EntityConverterUtils.parseId(identity.getId()); IdentityEntity entity = null; if (id > 0) { entity = getIdentityDAO().find(id); } else { entity = getIdentityDAO().findByProviderAndRemoteId(identity.getProviderId(), identity.getRemoteId()); } if (entity == null) { entity = new IdentityEntity(); } EntityConverterUtils.mapToEntity(identity, entity); if (entity.getId() > 0) { getIdentityDAO().update(entity); } else { if (identity.getProfile() != null) { mapToProfileEntity(identity.getProfile(), entity); } entity = getIdentityDAO().create(entity); } Profile profile = EntityConverterUtils.convertToProfile(entity, identity); if (id <= 0) { profile.setId(null); } identity.setProfile(profile); identity.setId(entity.getStringId()); } /** * Updates existing identity's properties. * * @param identity the identity to be updated. * @return the updated identity. * @throws IdentityStorageException if has any error * @since 1.2.0-GA */ public Identity updateIdentity(final Identity identity) throws IdentityStorageException { long id = EntityConverterUtils.parseId(identity.getId()); IdentityEntity entity = null; if (id > 0) { entity = getIdentityDAO().find(id); } if (entity == null) { throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_UPDATE_IDENTITY, "The identity does not exist on DB"); } EntityConverterUtils.mapToEntity(identity, entity); entity = getIdentityDAO().update(entity); return EntityConverterUtils.convertToIdentity(entity, true); } /** * Updates existing identity's membership in OrganizationService. * * @param remoteId the remoteId to be updated membership. * @throws IdentityStorageException if has any error * @since 4.0.0 */ public void updateIdentityMembership(final String remoteId) throws IdentityStorageException { // We do not need to implement this method, // only clear Identity Caching when user updated Group, what raised by Organization Service } /** * Gets the identity by his id. * * @param nodeId the id of identity * @return the identity * @throws IdentityStorageException if has any error */ @ExoTransactional public Identity findIdentityById(final String nodeId) throws IdentityStorageException { long id = EntityConverterUtils.parseId(nodeId); IdentityEntity entity = getIdentityDAO().find(id); if (entity != null) { return EntityConverterUtils.convertToIdentity(entity); } else { return null; } } /** * Deletes an identity from JCR * * @param identity the Identity to be deleted * @throws IdentityStorageException if has any error */ public void deleteIdentity(final Identity identity) throws IdentityStorageException { this.hardDeleteIdentity(identity); } /** * Hard delete an identity from JCR * * @param identity the identity to be deleted * @throws IdentityStorageException if has any error */ @ExoTransactional public void hardDeleteIdentity(final Identity identity) throws IdentityStorageException { long id = EntityConverterUtils.parseId(identity.getId()); String username = identity.getRemoteId(); String provider = identity.getProviderId(); IdentityEntity entity = getIdentityDAO().find(id); if (entity != null) { entity.setDeleted(true); getIdentityDAO().update(entity); } if (entity.getAvatarFileId() != null && entity.getAvatarFileId() > 0) { fileService.deleteFile(entity.getAvatarFileId()); } EntityManager em = CommonsUtils.getService(EntityManagerService.class).getEntityManager(); Query query; // Delete all connection query = em.createNamedQuery("SocConnection.deleteConnectionByIdentity"); query.setParameter("identityId", id); query.executeUpdate(); if (OrganizationIdentityProvider.NAME.equals(provider)) { // Delete space-member query = em.createNamedQuery("SpaceMember.deleteByUsername"); query.setParameter("username", username); query.executeUpdate(); } } /** * Load profile. * * @param profile the profile * @throws IdentityStorageException if has any error */ @ExoTransactional public Profile loadProfile(Profile profile) throws IdentityStorageException { long identityId = EntityConverterUtils.parseId(profile.getIdentity().getId()); IdentityEntity entity = identityDAO.find(identityId); if (entity == null) { return null; } else { profile.setId(String.valueOf(entity.getId())); EntityConverterUtils.mapToProfile(entity, profile); profile.clearHasChanged(); return profile; } } /** * Gets the identity by remote id. * * @param providerId the identity provider * @param remoteId the id * @return the identity by remote id * @throws IdentityStorageException if has any error */ @ExoTransactional public Identity findIdentity(final String providerId, final String remoteId) throws IdentityStorageException { try { IdentityEntity entity = getIdentityDAO().findByProviderAndRemoteId(providerId, remoteId); if (entity == null) { return null; } return EntityConverterUtils.convertToIdentity(entity); } catch (Exception ex) { throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_FIND_IDENTITY, "Can not load identity", ex); } } /** * Saves profile. * * @param profile the profile * @throws IdentityStorageException if has any error */ public void saveProfile(final Profile profile) throws IdentityStorageException { long id = EntityConverterUtils.parseId(profile.getIdentity().getId()); IdentityEntity entity = (id == 0 ? null : identityDAO.find(id)); if (entity == null) { throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_UPDATE_PROFILE, "Profile does not exist on RDBMS"); } else { mapToProfileEntity(profile, entity); identityDAO.update(entity); } profile.setId(entity.getStringId()); profile.clearHasChanged(); } /** * Updates profile. * * @param profile the profile * @throws IdentityStorageException if has any error * @since 1.2.0-GA */ public void updateProfile(final Profile profile) throws IdentityStorageException { long id = EntityConverterUtils.parseId(profile.getIdentity().getId()); IdentityEntity entity = identityDAO.find(id); if (entity == null) { throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_UPDATE_PROFILE, "Profile does not exist on RDBMS"); } else { mapToProfileEntity(profile, entity); identityDAO.update(entity); } } /** * Gets total number of identities in storage depend on providerId. * @throws IdentityStorageException if has any error */ public int getIdentitiesCount(final String providerId) throws IdentityStorageException { return (int) getIdentityDAO().countIdentityByProvider(providerId); } /** * Gets the type. * * @param nodetype the nodetype * @param property the property * @return the type * @throws IdentityStorageException if has any error */ public String getType(final String nodetype, final String property) { // This is not JCR implementation, so nodetype does not exist. return "undefined"; } /** * Add or modify properties of profile and persist to JCR. Profile parameter is a lightweight that * contains only the property that you want to add or modify. NOTE: The method will * not delete the properties on old profile when the param profile have not those keys. * * @param profile the profile * @throws IdentityStorageException if has any error */ public void addOrModifyProfileProperties(final Profile profile) throws IdentityStorageException { updateProfile(profile); } /** * Updates profile activity id by type. * * @param identity the identity * @param activityId the activity id * @param type Type of activity id to get. * @since 4.0.0.Alpha1 */ public void updateProfileActivityId(Identity identity, String activityId, Profile.AttachedActivityType type) { // Do not need to update in this case } /** * Gets profile activity id by type. * * @param profile the Profile * @param type Type of activity id to get. * @return Profile activity id. * @since 4.0.0.Alpha1 */ public String getProfileActivityId(Profile profile, Profile.AttachedActivityType type) { String t = "SPACE_ACTIVITY"; if (type == Profile.AttachedActivityType.USER) { t = "USER_PROFILE_ACTIVITY"; } else if (type == Profile.AttachedActivityType.RELATIONSHIP) { t = "USER_ACTIVITIES_FOR_RELATIONSHIP"; } List<ActivityEntity> activities = activityDAO.getActivitiesByPoster(profile.getIdentity(), 0, 1, t); if (activities != null && activities.size() > 0) { return String.valueOf(activities.get(0).getId()); } else { return null; } } /** * Gets the active user list base on the given ActiveIdentityFilter. * 1. N days who last login less than N days. * 2. UserGroup who belongs to this group. * * @param filter the filter * @return set of identity ids * @since 4.1.0 */ public Set<String> getActiveUsers(ActiveIdentityFilter filter) { Set<String> activeUsers = new HashSet<String>(); //by userGroups if (filter.getUserGroups() != null) { StringTokenizer stringToken = new StringTokenizer(filter.getUserGroups(), ActiveIdentityFilter.COMMA_SEPARATOR); try { while (stringToken.hasMoreTokens()) { try { ListAccess<User> listAccess = orgService.getUserHandler() .findUsersByGroupId(stringToken.nextToken().trim()); User[] users = listAccess.load(0, listAccess.getSize()); // for (User u : users) { activeUsers.add(u.getUserName()); } } catch (Exception e) { LOG.error(e.getMessage(), e); } } } catch (Exception e) { LOG.error(e.getMessage()); } } //by N days if (filter.getDays() > 0) { activeUsers = StorageUtils.getLastLogin(filter.getDays()); } //Gets online users and push to activate users if (CommonsUtils.getService(UserStateService.class) != null) { List<UserStateModel> onlines = CommonsUtils.getService(UserStateService.class).online(); for (UserStateModel user : onlines) { activeUsers.add(user.getUserId()); } } return activeUsers; } /** * Process enable/disable Identity * * @param identity The Identity enable * @param isEnable true if the user is enable, false if not * @since 4.2.x */ public void processEnabledIdentity(Identity identity, boolean isEnable) { long id = EntityConverterUtils.parseId(identity.getId()); IdentityEntity entity = getIdentityDAO().find(id); if (entity == null) { throw new IllegalArgumentException("Identity does not exists"); } entity.setEnabled(isEnable); getIdentityDAO().update(entity); } @Override public List<Identity> getIdentitiesByFirstCharacterOfName(String providerId, ProfileFilter profileFilter, long offset, long limit, boolean forceLoadOrReloadProfile) throws IdentityStorageException { return getIdentitiesByProfileFilter(providerId, profileFilter, offset, limit, forceLoadOrReloadProfile); } @Override public List<Identity> getIdentitiesForMentions(String providerId, ProfileFilter profileFilter, long offset, long limit, boolean forceLoadOrReloadProfile) throws IdentityStorageException { return getIdentitiesByProfileFilter(providerId, profileFilter, offset, limit, forceLoadOrReloadProfile); } @Override public int getIdentitiesByProfileFilterCount(String providerId, ProfileFilter profileFilter) throws IdentityStorageException { ExtendProfileFilter xFilter = new ExtendProfileFilter(profileFilter); ListAccess<IdentityEntity> list = getIdentityDAO().findIdentities(xFilter); try { return list.getSize(); } catch (Exception e) { return 0; } } @Override public int getIdentitiesByFirstCharacterOfNameCount(String providerId, ProfileFilter profileFilter) throws IdentityStorageException { ExtendProfileFilter xFilter = new ExtendProfileFilter(profileFilter); xFilter.setProviderId(providerId); ListAccess<IdentityEntity> list = getIdentityDAO().findIdentities(xFilter); try { return list.getSize(); } catch (Exception ex) { return 0; } } public List<Identity> getIdentitiesForUnifiedSearch(final String providerId, final ProfileFilter profileFilter, long offset, long limit) throws IdentityStorageException { return profileSearchConnector.search(null, profileFilter, null, offset, limit); } public List<Identity> getSpaceMemberIdentitiesByProfileFilter(final Space space, final ProfileFilter profileFilter, SpaceMemberFilterListAccess.Type type, long offset, long limit) throws IdentityStorageException { List<Long> relations = new ArrayList<>(); if (space != null) { try { SpaceEntity gotSpace = spaceDAO.find(Long.parseLong(space.getId())); String[] members = null; switch (type) { case MEMBER: members = gotSpace.getMembersId(); break; case MANAGER: members = gotSpace.getManagerMembersId(); List<String> wildcardUsers = SpaceUtils.findMembershipUsersByGroupAndTypes(space.getGroupId(), MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); for (String remoteId : wildcardUsers) { Identity id = findIdentity(OrganizationIdentityProvider.NAME, remoteId); if (id != null) { relations.add(EntityConverterUtils.parseId(id.getId())); } } break; } for (int i = 0; i < members.length; i++) { Identity identity = findIdentity(OrganizationIdentityProvider.NAME, members[i]); if (identity != null) { relations.add(EntityConverterUtils.parseId(identity.getId())); } } } catch (IdentityStorageException e) { throw new IdentityStorageException(IdentityStorageException.Type.FAIL_TO_FIND_IDENTITY); } if (relations.isEmpty()) { relations.add(-1L); } } ExtendProfileFilter xFilter = new ExtendProfileFilter(profileFilter); xFilter.setIdentityIds(relations); ListAccess<IdentityEntity> list = getIdentityDAO().findIdentities(xFilter); return EntityConverterUtils.convertToIdentities(list, offset, limit); } public List<Identity> getIdentitiesByProfileFilter(final String providerId, final ProfileFilter profileFilter, long offset, long limit, boolean forceLoadOrReloadProfile) throws IdentityStorageException { ExtendProfileFilter xFilter = new ExtendProfileFilter(profileFilter); xFilter.setProviderId(providerId); xFilter.setForceLoadProfile(forceLoadOrReloadProfile); ListAccess<IdentityEntity> list = getIdentityDAO().findIdentities(xFilter); return EntityConverterUtils.convertToIdentities(list, offset, limit); } @Override public List<IdentityWithRelationship> getIdentitiesWithRelationships(final String identityId, int offset, int limit) throws IdentityStorageException { ListAccess<Entry<IdentityEntity, ConnectionEntity>> list = getIdentityDAO() .findAllIdentitiesWithConnections(Long.valueOf(identityId)); return EntityConverterUtils.convertToIdentitiesWithRelationship(list, offset, limit); } @Override public int countIdentitiesWithRelationships(String identityId) throws Exception { ListAccess<Entry<IdentityEntity, ConnectionEntity>> list = getIdentityDAO() .findAllIdentitiesWithConnections(Long.valueOf(identityId)); return list.getSize(); } public ListAccess<Identity> findByFilter(ExtendProfileFilter filter) { final ListAccess<IdentityEntity> list = getIdentityDAO().findIdentities(filter); return new ListAccess<Identity>() { @Override public Identity[] load(int offset, int size) throws Exception, IllegalArgumentException { IdentityEntity[] entities = list.load(offset, size); if (entities == null || entities.length == 0) { return new Identity[0]; } else { Identity[] identities = new Identity[entities.length]; for (int i = 0; i < entities.length; i++) { identities[i] = EntityConverterUtils.convertToIdentity(entities[i]); } return identities; } } @Override public int getSize() throws Exception { return list.getSize(); } }; } /** * This method is introduced to clean totally identity from database * It's used in unit test * @param identity the Identity */ @ExoTransactional public void removeIdentity(Identity identity) { long id = EntityConverterUtils.parseId(identity.getId()); String username = identity.getRemoteId(); String provider = identity.getProviderId(); IdentityEntity entity = getIdentityDAO().find(id); EntityManager em = CommonsUtils.getService(EntityManagerService.class).getEntityManager(); Query query; // Delete all connection query = em.createNamedQuery("SocConnection.deleteConnectionByIdentity"); query.setParameter("identityId", id); query.executeUpdate(); if (OrganizationIdentityProvider.NAME.equals(provider)) { // Delete space-member query = em.createNamedQuery("SpaceMember.deleteByUsername"); query.setParameter("username", username); query.executeUpdate(); } if (entity != null) { getIdentityDAO().delete(entity); } } }