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 <[email protected]> 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.ComponentManager; 047import org.nuxeo.runtime.model.DefaultComponent; 048 049import com.google.common.cache.Cache; 050import com.google.common.cache.CacheBuilder; 051 052/** 053 * Implementation of {@code UserProfileService}. 054 * 055 * @see UserProfileService 056 * @author <a href="mailto:[email protected]">Quentin Lamerand</a> 057 * @since 5.5 058 */ 059public class UserProfileServiceImpl extends DefaultComponent implements UserProfileService { 060 061 private static final Log log = LogFactory.getLog(UserProfileServiceImpl.class); 062 063 protected static final Integer CACHE_CONCURRENCY_LEVEL = 10; 064 065 protected static final Integer CACHE_TIMEOUT = 10; 066 067 protected static final Integer CACHE_MAXIMUM_SIZE = 1000; 068 069 public static final String CONFIG_EP = "config"; 070 071 private ImporterConfig config; 072 073 private UserWorkspaceService userWorkspaceService; 074 075 protected final Cache<String, String> profileUidCache = CacheBuilder.newBuilder().concurrencyLevel( 076 CACHE_CONCURRENCY_LEVEL).maximumSize(CACHE_MAXIMUM_SIZE).expireAfterWrite(CACHE_TIMEOUT, TimeUnit.MINUTES).build(); 077 078 @Override 079 public DocumentModel getUserProfileDocument(CoreSession session) { 080 DocumentModel userWorkspace = getUserWorkspaceService().getCurrentUserPersonalWorkspace(session, null); 081 if (userWorkspace == null) { 082 return null; 083 } 084 085 String uid = profileUidCache.getIfPresent(session.getPrincipal().getName()); 086 final IdRef ref = new IdRef(uid); 087 if (uid != null && session.exists(ref)) { 088 return session.getDocument(ref); 089 } else { 090 DocumentModel profile = new UserProfileDocumentGetter(session, userWorkspace).getOrCreate(); 091 profileUidCache.put(session.getPrincipal().getName(), profile.getId()); 092 return profile; 093 } 094 } 095 096 @Override 097 public DocumentModel getUserProfileDocument(String userName, CoreSession session) { 098 DocumentModel userWorkspace = getUserWorkspaceService().getUserPersonalWorkspace(userName, 099 session.getRootDocument()); 100 if (userWorkspace == null) { 101 return null; 102 } 103 104 String uid = profileUidCache.getIfPresent(userName); 105 final IdRef ref = new IdRef(uid); 106 if (uid != null && session.exists(ref)) { 107 return session.getDocument(ref); 108 } else { 109 DocumentModel profile = new UserProfileDocumentGetter(session, userWorkspace).getOrCreate(); 110 profileUidCache.put(userName, profile.getId()); 111 return profile; 112 } 113 } 114 115 @Override 116 public DocumentModel getUserProfile(DocumentModel userModel, CoreSession session) { 117 DocumentModel userProfileDoc = getUserProfileDocument(userModel.getId(), session); 118 if (userProfileDoc == null) { 119 return null; 120 } 121 122 userProfileDoc.detach(true); 123 userProfileDoc.getDataModels().putAll(userModel.getDataModels()); 124 return userProfileDoc; 125 } 126 127 private UserWorkspaceService getUserWorkspaceService() { 128 if (userWorkspaceService == null) { 129 userWorkspaceService = Framework.getService(UserWorkspaceService.class); 130 } 131 return userWorkspaceService; 132 } 133 134 private class UserProfileDocumentGetter extends UnrestrictedSessionRunner { 135 136 private DocumentModel userWorkspace; 137 138 private DocumentRef userProfileDocRef; 139 140 public UserProfileDocumentGetter(CoreSession session, DocumentModel userWorkspace) { 141 super(session); 142 this.userWorkspace = userWorkspace; 143 } 144 145 @Override 146 public void run() { 147 148 String query = "select * from " + USER_PROFILE_DOCTYPE + " where ecm:parentId='" + userWorkspace.getId() 149 + "' " + " AND ecm:isProxy = 0 " 150 + " AND ecm:isVersion = 0 AND ecm:isTrashed = 0"; 151 DocumentModelList children = session.query(query); 152 if (!children.isEmpty()) { 153 userProfileDocRef = children.get(0).getRef(); 154 } else { 155 DocumentModel userProfileDoc = session.createDocumentModel(userWorkspace.getPathAsString(), 156 String.valueOf(System.currentTimeMillis()), USER_PROFILE_DOCTYPE); 157 userProfileDoc = session.createDocument(userProfileDoc); 158 userProfileDocRef = userProfileDoc.getRef(); 159 ACP acp = session.getACP(userProfileDocRef); 160 ACL acl = acp.getOrCreateACL(); 161 acl.add(new ACE(EVERYONE, READ, true)); 162 acp.addACL(acl); 163 session.setACP(userProfileDocRef, acp, true); 164 session.save(); 165 } 166 } 167 168 public DocumentModel getOrCreate() { 169 if (session.hasPermission(userWorkspace.getRef(), ADD_CHILDREN)) { 170 run(); 171 } else { 172 runUnrestricted(); 173 } 174 return session.getDocument(userProfileDocRef); 175 } 176 } 177 178 @Override 179 public void clearCache() { 180 profileUidCache.invalidateAll(); 181 } 182 183 @Override 184 public ImporterConfig getImporterConfig() { 185 return config; 186 } 187 188 @Override 189 public int getApplicationStartedOrder() { 190 return 101; // after RepositoryService 191 } 192 193 @Override 194 public void start(ComponentContext context) { 195 if (config == null || config.getDataFileName() == null) { 196 return; 197 } 198 Framework.getRuntime().getComponentManager().addListener(new ComponentManager.Listener() { 199 @Override 200 public void afterStart(ComponentManager mgr, boolean isResume) { 201 // needs to run after RepositoryInitializationHandlers, run by RepositoryService 202 scheduleImport(); 203 } 204 205 @Override 206 public void afterStop(ComponentManager mgr, boolean isStandby) { 207 Framework.getRuntime().getComponentManager().removeListener(this); 208 } 209 }); 210 } 211 212 protected void scheduleImport() { 213 WorkManager wm = Framework.getService(WorkManager.class); 214 if (wm != null) { 215 wm.schedule(new UserProfileImporterWork(), true); 216 } 217 } 218 219 @Override 220 public void registerContribution(Object contribution, 221 String extensionPoint, ComponentInstance contributor) { 222 if (CONFIG_EP.equals(extensionPoint)) { 223 if (config != null) { 224 log.warn("Overriding existing user profile importer config"); 225 } 226 config = (ImporterConfig) contribution; 227 } 228 } 229 230 @Override 231 public void unregisterContribution(Object contribution, 232 String extensionPoint, ComponentInstance contributor) { 233 if (CONFIG_EP.equals(extensionPoint)) { 234 if (config != null && config.equals(contribution)) { 235 config = null; 236 } 237 } 238 } 239}