001/* 002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Quentin Lamerand <qlamerand@nuxeo.com> 016 */ 017 018package org.nuxeo.ecm.user.center.profile; 019 020import static org.nuxeo.ecm.core.api.security.SecurityConstants.ADD_CHILDREN; 021import static org.nuxeo.ecm.core.api.security.SecurityConstants.EVERYONE; 022import static org.nuxeo.ecm.core.api.security.SecurityConstants.READ; 023import static org.nuxeo.ecm.user.center.profile.UserProfileConstants.USER_PROFILE_DOCTYPE; 024 025import java.util.concurrent.TimeUnit; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.nuxeo.ecm.core.api.CoreSession; 030import org.nuxeo.ecm.core.api.DocumentModel; 031import org.nuxeo.ecm.core.api.DocumentModelList; 032import org.nuxeo.ecm.core.api.DocumentRef; 033import org.nuxeo.ecm.core.api.IdRef; 034import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 035import org.nuxeo.ecm.core.api.security.ACE; 036import org.nuxeo.ecm.core.api.security.ACL; 037import org.nuxeo.ecm.core.api.security.ACP; 038import org.nuxeo.ecm.core.work.api.WorkManager; 039import org.nuxeo.ecm.core.work.api.WorkManager.Scheduling; 040import org.nuxeo.ecm.platform.userworkspace.api.UserWorkspaceService; 041import org.nuxeo.runtime.api.Framework; 042import org.nuxeo.runtime.model.ComponentContext; 043import org.nuxeo.runtime.model.ComponentInstance; 044import org.nuxeo.runtime.model.DefaultComponent; 045 046import com.google.common.cache.Cache; 047import com.google.common.cache.CacheBuilder; 048 049/** 050 * Implementation of {@code UserProfileService}. 051 * 052 * @see UserProfileService 053 * @author <a href="mailto:qlamerand@nuxeo.com">Quentin Lamerand</a> 054 * @since 5.5 055 */ 056public class UserProfileServiceImpl extends DefaultComponent implements UserProfileService { 057 058 private static final Log log = LogFactory.getLog(UserProfileServiceImpl.class); 059 060 protected static final Integer CACHE_CONCURRENCY_LEVEL = 10; 061 062 protected static final Integer CACHE_TIMEOUT = 10; 063 064 protected static final Integer CACHE_MAXIMUM_SIZE = 1000; 065 066 public static final String CONFIG_EP = "config"; 067 068 private ImporterConfig config; 069 070 private UserWorkspaceService userWorkspaceService; 071 072 protected final Cache<String, String> profileUidCache = CacheBuilder.newBuilder().concurrencyLevel( 073 CACHE_CONCURRENCY_LEVEL).maximumSize(CACHE_MAXIMUM_SIZE).expireAfterWrite(CACHE_TIMEOUT, TimeUnit.MINUTES).build(); 074 075 @Override 076 public DocumentModel getUserProfileDocument(CoreSession session) { 077 DocumentModel userWorkspace = getUserWorkspaceService().getCurrentUserPersonalWorkspace(session, null); 078 String uid = profileUidCache.getIfPresent(session.getPrincipal().getName()); 079 final IdRef ref = new IdRef(uid); 080 if (uid != null && session.exists(ref)) { 081 return session.getDocument(ref); 082 } else { 083 DocumentModel profile = new UserProfileDocumentGetter(session, userWorkspace).getOrCreate(); 084 profileUidCache.put(session.getPrincipal().getName(), profile.getId()); 085 return profile; 086 } 087 } 088 089 @Override 090 public DocumentModel getUserProfileDocument(String userName, CoreSession session) { 091 DocumentModel userWorkspace = getUserWorkspaceService().getUserPersonalWorkspace(userName, 092 session.getRootDocument()); 093 094 String uid = profileUidCache.getIfPresent(userName); 095 final IdRef ref = new IdRef(uid); 096 if (uid != null && session.exists(ref)) { 097 return session.getDocument(ref); 098 } else { 099 DocumentModel profile = new UserProfileDocumentGetter(session, userWorkspace).getOrCreate(); 100 profileUidCache.put(userName, profile.getId()); 101 return profile; 102 } 103 } 104 105 @Override 106 public DocumentModel getUserProfile(DocumentModel userModel, CoreSession session) { 107 DocumentModel userProfileDoc = getUserProfileDocument(userModel.getId(), session); 108 userProfileDoc.detach(true); 109 userProfileDoc.getDataModels().putAll(userModel.getDataModels()); 110 return userProfileDoc; 111 } 112 113 private UserWorkspaceService getUserWorkspaceService() { 114 if (userWorkspaceService == null) { 115 userWorkspaceService = Framework.getLocalService(UserWorkspaceService.class); 116 } 117 return userWorkspaceService; 118 } 119 120 private class UserProfileDocumentGetter extends UnrestrictedSessionRunner { 121 122 private DocumentModel userWorkspace; 123 124 private DocumentRef userProfileDocRef; 125 126 public UserProfileDocumentGetter(CoreSession session, DocumentModel userWorkspace) { 127 super(session); 128 this.userWorkspace = userWorkspace; 129 } 130 131 @Override 132 public void run() { 133 134 String query = "select * from " + USER_PROFILE_DOCTYPE + " where ecm:parentId='" + userWorkspace.getId() 135 + "' " + " AND ecm:isProxy = 0 " 136 + " AND ecm:isCheckedInVersion = 0 AND ecm:currentLifeCycleState != 'deleted'"; 137 DocumentModelList children = session.query(query); 138 if (!children.isEmpty()) { 139 userProfileDocRef = children.get(0).getRef(); 140 } else { 141 DocumentModel userProfileDoc = session.createDocumentModel(userWorkspace.getPathAsString(), 142 String.valueOf(System.currentTimeMillis()), USER_PROFILE_DOCTYPE); 143 userProfileDoc = session.createDocument(userProfileDoc); 144 userProfileDocRef = userProfileDoc.getRef(); 145 ACP acp = session.getACP(userProfileDocRef); 146 ACL acl = acp.getOrCreateACL(); 147 acl.add(new ACE(EVERYONE, READ, true)); 148 acp.addACL(acl); 149 session.setACP(userProfileDocRef, acp, true); 150 session.save(); 151 } 152 } 153 154 public DocumentModel getOrCreate() { 155 if (session.hasPermission(userWorkspace.getRef(), ADD_CHILDREN)) { 156 run(); 157 } else { 158 runUnrestricted(); 159 } 160 return session.getDocument(userProfileDocRef); 161 } 162 } 163 164 @Override 165 public void clearCache() { 166 profileUidCache.invalidateAll(); 167 } 168 169 @Override 170 public ImporterConfig getImporterConfig() { 171 return config; 172 } 173 174 @Override 175 public void applicationStarted(ComponentContext context) { 176 if (config == null || config.getDataFileName() == null) { 177 return; 178 } 179 WorkManager wm = Framework.getService(WorkManager.class); 180 if (wm!=null) { 181 wm.schedule(new UserProfileImporterWork(), Scheduling.IF_NOT_RUNNING_OR_SCHEDULED, true); 182 } 183 } 184 185 @Override 186 public void registerContribution(Object contribution, 187 String extensionPoint, ComponentInstance contributor) { 188 if (CONFIG_EP.equals(extensionPoint)) { 189 if (config != null) { 190 log.warn("Overriding existing user profile importer config"); 191 } 192 config = (ImporterConfig) contribution; 193 } 194 } 195 196 @Override 197 public void unregisterContribution(Object contribution, 198 String extensionPoint, ComponentInstance contributor) { 199 if (CONFIG_EP.equals(extensionPoint)) { 200 if (config != null && config.equals(contribution)) { 201 config = null; 202 } 203 } 204 } 205}