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