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