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