001/* 002 * (C) Copyright 2006-2014 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 * Bogdan Stefanescu 018 * Florent Guillaume 019 */ 020package org.nuxeo.ecm.core.api; 021 022import static org.nuxeo.ecm.core.api.security.SecurityConstants.SYSTEM_USERNAME; 023 024import java.io.Serializable; 025import java.security.Principal; 026import java.util.ArrayList; 027import java.util.Collection; 028import java.util.Map; 029import java.util.concurrent.ConcurrentHashMap; 030 031import org.nuxeo.ecm.core.api.impl.UserPrincipal; 032import org.nuxeo.ecm.core.api.local.ClientLoginModule; 033import org.nuxeo.ecm.core.api.local.LoginStack; 034import org.nuxeo.ecm.core.api.repository.RepositoryManager; 035import org.nuxeo.ecm.core.api.security.SecurityConstants; 036import org.nuxeo.runtime.api.Framework; 037import org.nuxeo.runtime.api.login.LoginComponent; 038 039import com.google.common.base.Predicate; 040import com.google.common.collect.Collections2; 041 042/** 043 * The CoreInstance is the main access point to a CoreSession. 044 */ 045public class CoreInstance { 046 047 private static final CoreInstance INSTANCE = new CoreInstance(); 048 049 public static class RegistrationInfo extends Throwable { 050 051 private static final long serialVersionUID = 1L; 052 053 public final CoreSession session; 054 055 public RegistrationInfo(CoreSession session) { 056 super("RegistrationInfo(" + Thread.currentThread().getName() + ", " + session.getSessionId() + ")"); 057 this.session = session; 058 } 059 060 } 061 062 /** 063 * All open CoreSessionInfo, keyed by session id. 064 */ 065 private final Map<String, RegistrationInfo> sessions = new ConcurrentHashMap<String, RegistrationInfo>(); 066 067 private CoreInstance() { 068 } 069 070 /** 071 * Gets the CoreInstance singleton. 072 */ 073 public static CoreInstance getInstance() { 074 return INSTANCE; 075 } 076 077 /** 078 * Opens a {@link CoreSession} for the currently logged-in user. 079 * <p> 080 * The session must be closed using {@link CoreSession#close}. 081 * 082 * @param repositoryName the repository name, or {@code null} for the default repository 083 * @return the session 084 * @since 5.9.3 085 */ 086 public static CoreSession openCoreSession(String repositoryName) { 087 return openCoreSession(repositoryName, getPrincipal((String) null)); 088 } 089 090 /** 091 * MUST ONLY BE USED IN UNIT TESTS to open a {@link CoreSession} for the given user. 092 * <p> 093 * The session must be closed using {@link CoreSession#close}. 094 * 095 * @param repositoryName the repository name, or {@code null} for the default repository 096 * @param username the user name 097 * @return the session 098 * @since 5.9.3 099 */ 100 public static CoreSession openCoreSession(String repositoryName, String username) { 101 return openCoreSession(repositoryName, getPrincipal(username)); 102 } 103 104 /** 105 * Opens a {@link CoreSession} for a system user. 106 * <p> 107 * The session must be closed using {@link CoreSession#close}. 108 * 109 * @param repositoryName the repository name, or {@code null} for the default repository 110 * @return the session 111 * @since 5.9.3 112 */ 113 public static CoreSession openCoreSessionSystem(String repositoryName) { 114 return openCoreSession(repositoryName, getPrincipal((SecurityConstants.SYSTEM_USERNAME))); 115 } 116 117 /** 118 * Opens a {@link CoreSession} for a system user with an optional originating username. 119 * <p> 120 * The session must be closed using {@link CoreSession#close}. 121 * 122 * @param repositoryName the repository name, or {@code null} for the default repository 123 * @param originatingUsername the originating username to set on the SystemPrincipal 124 * @return the session 125 * @since 8.1 126 */ 127 public static CoreSession openCoreSessionSystem(String repositoryName, String originatingUsername) { 128 NuxeoPrincipal principal = getPrincipal((SecurityConstants.SYSTEM_USERNAME)); 129 principal.setOriginatingUser(originatingUsername); 130 return openCoreSession(repositoryName, principal); 131 } 132 133 /** 134 * @deprecated since 5.9.3, use {@link #openCoreSession} instead. 135 */ 136 @Deprecated 137 public CoreSession open(String repositoryName, Map<String, Serializable> context) { 138 return openCoreSession(repositoryName, getPrincipal(context)); 139 } 140 141 /** 142 * NOT PUBLIC, DO NOT CALL. Kept public for compatibility with old code. 143 * <p> 144 * Opens a {@link CoreSession} for the given context. 145 * 146 * @param repositoryName the repository name, or {@code null} for the default repository 147 * @param context the session open context 148 * @return the session 149 */ 150 public static CoreSession openCoreSession(String repositoryName, Map<String, Serializable> context) { 151 return openCoreSession(repositoryName, getPrincipal(context)); 152 } 153 154 /** 155 * MUST ONLY BE USED IN UNIT TESTS to open a {@link CoreSession} for the given principal. 156 * <p> 157 * The session must be closed using {@link CoreSession#close}. 158 * 159 * @param repositoryName the repository name, or {@code null} for the default repository 160 * @param principal the principal 161 * @return the session 162 * @since 5.9.3 163 */ 164 public static CoreSession openCoreSession(String repositoryName, Principal principal) { 165 if (principal instanceof NuxeoPrincipal) { 166 return openCoreSession(repositoryName, (NuxeoPrincipal) principal); 167 } else { 168 return openCoreSession(repositoryName, getPrincipal(principal.getName())); 169 } 170 } 171 172 /** 173 * MUST ONLY BE USED IN UNIT TESTS to open a {@link CoreSession} for the given principal. 174 * <p> 175 * The session must be closed using {@link CoreSession#close}. 176 * 177 * @param repositoryName the repository name, or {@code null} for the default repository 178 * @param principal the principal 179 * @return the session 180 * @since 5.9.3 181 */ 182 public static CoreSession openCoreSession(String repositoryName, NuxeoPrincipal principal) { 183 if (repositoryName == null) { 184 RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class); 185 repositoryName = repositoryManager.getDefaultRepository().getName(); 186 } 187 return getInstance().acquireCoreSession(repositoryName, principal); 188 } 189 190 protected CoreSession acquireCoreSession(String repositoryName, NuxeoPrincipal principal) { 191 CoreSession session = Framework.getLocalService(CoreSession.class); 192 if (session == null) { 193 throw new NuxeoException("RepositoryService failed to start"); 194 } 195 session.connect(repositoryName, principal); 196 sessions.put(session.getSessionId(), new RegistrationInfo(session)); 197 return session; 198 } 199 200 /** 201 * Gets an existing open session for the given session id. 202 * <p> 203 * The returned CoreSession must not be closed, as it is owned by someone else. 204 * 205 * @param sessionId the session id 206 * @return the session, which must not be closed 207 */ 208 public CoreSession getSession(String sessionId) { 209 if (sessionId == null) { 210 throw new NullPointerException("null sessionId"); 211 } 212 RegistrationInfo csi = sessions.get(sessionId); 213 return csi == null ? null : csi.session; 214 } 215 216 /** 217 * Use {@link CoreSession#close} instead. 218 * 219 * @since 5.9.3 220 */ 221 public static void closeCoreSession(CoreSession session) { 222 getInstance().releaseCoreSession(session); 223 } 224 225 protected void releaseCoreSession(CoreSession session) { 226 String sessionId = session.getSessionId(); 227 RegistrationInfo csi = sessions.remove(sessionId); 228 if (csi == null) { 229 throw new RuntimeException("Closing unknown CoreSession: " + sessionId, csi); 230 } 231 session.destroy(); 232 } 233 234 protected static NuxeoPrincipal getPrincipal(Map<String, Serializable> map) { 235 if (map == null) { 236 return getPrincipal((String) null); // logged-in principal 237 } 238 NuxeoPrincipal principal = (NuxeoPrincipal) map.get("principal"); 239 if (principal == null) { 240 principal = getPrincipal((String) map.get("username")); 241 } 242 return principal; 243 } 244 245 protected static NuxeoPrincipal getPrincipal(String username) { 246 if (username != null) { 247 if (SYSTEM_USERNAME.equals(username)) { 248 return new SystemPrincipal(null); 249 } else { 250 return new UserPrincipal(username, new ArrayList<String>(), false, false); 251 } 252 } else { 253 LoginStack.Entry entry = ClientLoginModule.getCurrentLogin(); 254 if (entry != null) { 255 Principal p = entry.getPrincipal(); 256 if (p instanceof NuxeoPrincipal) { 257 return (NuxeoPrincipal) p; 258 } else if (LoginComponent.isSystemLogin(p)) { 259 return new SystemPrincipal(p.getName()); 260 } else { 261 throw new RuntimeException("Unsupported principal: " + p.getClass()); 262 } 263 } else { 264 if (Framework.isTestModeSet()) { 265 return new SystemPrincipal(null); 266 } else { 267 throw new NuxeoException("Cannot create a CoreSession outside a security context, " 268 + " login() missing."); 269 } 270 } 271 } 272 } 273 274 /** 275 * @deprecated since 5.9.3, use {@link CoreSession#close} instead. 276 */ 277 @Deprecated 278 public void close(CoreSession session) { 279 session.close(); // calls back closeCoreSession 280 } 281 282 /** 283 * Gets the number of open sessions. 284 * 285 * @since 5.4.2 286 */ 287 public int getNumberOfSessions() { 288 return sessions.size(); 289 } 290 291 public Collection<RegistrationInfo> getRegistrationInfos() { 292 return new ArrayList<>(sessions.values()); 293 } 294 295 public Collection<RegistrationInfo> getRegistrationInfosLive(final boolean onThread) { 296 return Collections2.filter(sessions.values(), new Predicate<RegistrationInfo>() { 297 298 @Override 299 public boolean apply(RegistrationInfo input) { 300 return input.session.isLive(onThread); 301 } 302 303 }); 304 } 305 306 public void cleanupThisThread() { 307 cleanup(true); 308 } 309 310 public void cleanup(boolean onthread) { 311 NuxeoException errors = new NuxeoException("disconnecting from storage for you"); 312 for (RegistrationInfo each : CoreInstance.getInstance().getRegistrationInfosLive(false)) { 313 each.session.destroy(); 314 errors.addSuppressed(each); 315 } 316 if (errors.getSuppressed().length > 0) { 317 throw errors; 318 } 319 } 320}