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