/*
* Copyright (c) 2006-2014 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Bogdan Stefanescu
* Florent Guillaume
*/
package org.nuxeo.ecm.core.api;
import static org.nuxeo.ecm.core.api.security.SecurityConstants.SYSTEM_USERNAME;
import java.io.Serializable;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.nuxeo.ecm.core.api.impl.UserPrincipal;
import org.nuxeo.ecm.core.api.local.ClientLoginModule;
import org.nuxeo.ecm.core.api.local.LoginStack;
import org.nuxeo.ecm.core.api.repository.RepositoryManager;
import org.nuxeo.ecm.core.api.security.SecurityConstants;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.api.login.LoginComponent;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
/**
* The CoreInstance is the main access point to a CoreSession.
*/
public class CoreInstance {
private static final CoreInstance INSTANCE = new CoreInstance();
public static class RegistrationInfo extends Throwable {
private static final long serialVersionUID = 1L;
public final CoreSession session;
public RegistrationInfo(CoreSession session) {
super("RegistrationInfo(" + Thread.currentThread().getName() + ", "
+ session.getSessionId() + ")");
this.session = session;
}
}
/**
* All open CoreSessionInfo, keyed by session id.
*/
private final Map<String, RegistrationInfo> sessions = new ConcurrentHashMap<String, RegistrationInfo>();
private CoreInstance() {
}
/**
* Gets the CoreInstance singleton.
*/
public static CoreInstance getInstance() {
return INSTANCE;
}
/**
* Opens a {@link CoreSession} for the currently logged-in user.
* <p>
* The session must be closed using {@link CoreSession#close}.
*
* @param repositoryName the repository name, or {@code null} for the
* default repository
* @return the session
* @since 5.9.3
*/
public static CoreSession openCoreSession(String repositoryName)
throws ClientException {
return openCoreSession(repositoryName, getPrincipal((String) null));
}
/**
* Opens a {@link CoreSession} for the given user.
* <p>
* The session must be closed using {@link CoreSession#close}.
*
* @param repositoryName the repository name, or {@code null} for the
* default repository
* @param username the user name
* @return the session
* @since 5.9.3
*/
public static CoreSession openCoreSession(String repositoryName,
String username) throws ClientException {
return openCoreSession(repositoryName, getPrincipal(username));
}
/**
* Opens a {@link CoreSession} for a system user.
* <p>
* The session must be closed using {@link CoreSession#close}.
*
* @param repositoryName the repository name, or {@code null} for the
* default repository
* @return the session
* @since 5.9.3
*/
public static CoreSession openCoreSessionSystem(String repositoryName)
throws ClientException {
return openCoreSession(repositoryName,
getPrincipal((SecurityConstants.SYSTEM_USERNAME)));
}
/**
* @deprecated since 5.9.3, use {@link #openCoreSession} instead.
*/
@Deprecated
public CoreSession open(String repositoryName,
Map<String, Serializable> context) throws ClientException {
return openCoreSession(repositoryName, getPrincipal(context));
}
/**
* NOT PUBLIC, DO NOT CALL. Kept public for compatibility with old code.
* <p>
* Opens a {@link CoreSession} for the given context.
*
* @param repositoryName the repository name, or {@code null} for the
* default repository
* @param context the session open context
* @return the session
*/
public static CoreSession openCoreSession(String repositoryName,
Map<String, Serializable> context) throws ClientException {
return openCoreSession(repositoryName, getPrincipal(context));
}
/**
* Opens a {@link CoreSession} for the given principal.
* <p>
* The session must be closed using {@link CoreSession#close}.
*
* @param repositoryName the repository name, or {@code null} for the
* default repository
* @param principal the principal
* @return the session
* @since 5.9.3
*/
public static CoreSession openCoreSession(String repositoryName,
Principal principal) throws ClientException {
if (principal instanceof NuxeoPrincipal) {
return openCoreSession(repositoryName, (NuxeoPrincipal) principal);
} else {
return openCoreSession(repositoryName,
getPrincipal(principal.getName()));
}
}
/**
* Opens a {@link CoreSession} for the given principal.
* <p>
* The session must be closed using {@link CoreSession#close}.
*
* @param repositoryName the repository name, or {@code null} for the
* default repository
* @param principal the principal
* @return the session
* @since 5.9.3
*/
public static CoreSession openCoreSession(String repositoryName,
NuxeoPrincipal principal) throws ClientException {
if (repositoryName == null) {
RepositoryManager repositoryManager = Framework.getLocalService(RepositoryManager.class);
repositoryName = repositoryManager.getDefaultRepository().getName();
}
return getInstance().acquireCoreSession(repositoryName, principal);
}
protected CoreSession acquireCoreSession(String repositoryName,
NuxeoPrincipal principal) throws ClientException {
CoreSession session = Framework.getLocalService(CoreSession.class);
session.connect(repositoryName, principal);
sessions.put(session.getSessionId(), new RegistrationInfo(session));
return session;
}
/**
* Gets an existing open session for the given session id.
* <p>
* The returned CoreSession must not be closed, as it is owned by someone
* else.
*
* @param sessionId the session id
* @return the session, which must not be closed
*/
public CoreSession getSession(String sessionId) {
if (sessionId == null) {
throw new NullPointerException("null sessionId");
}
RegistrationInfo csi = sessions.get(sessionId);
return csi == null ? null : csi.session;
}
/**
* Use {@link CoreSession#close} instead.
*
* @since 5.9.3
*/
public static void closeCoreSession(CoreSession session) {
getInstance().releaseCoreSession(session);
}
protected void releaseCoreSession(CoreSession session) {
String sessionId = session.getSessionId();
RegistrationInfo csi = sessions.remove(sessionId);
if (csi == null) {
throw new RuntimeException("Closing unknown CoreSession: "
+ sessionId);
}
session.destroy();
}
protected static NuxeoPrincipal getPrincipal(Map<String, Serializable> map)
throws ClientException {
if (map == null) {
return getPrincipal((String) null); // logged-in principal
}
NuxeoPrincipal principal = (NuxeoPrincipal) map.get("principal");
if (principal == null) {
principal = getPrincipal((String) map.get("username"));
}
return principal;
}
protected static NuxeoPrincipal getPrincipal(String username)
throws ClientException {
if (username != null) {
if (SYSTEM_USERNAME.equals(username)) {
return new SystemPrincipal(null);
} else {
return new UserPrincipal(username, new ArrayList<String>(),
false, false);
}
} else {
LoginStack.Entry entry = ClientLoginModule.getCurrentLogin();
if (entry != null) {
Principal p = entry.getPrincipal();
if (p instanceof NuxeoPrincipal) {
return (NuxeoPrincipal) p;
} else if (LoginComponent.isSystemLogin(p)) {
return new SystemPrincipal(p.getName());
} else {
throw new RuntimeException("Unsupported principal: "
+ p.getClass());
}
} else {
if (Framework.isTestModeSet()) {
return new SystemPrincipal(null);
} else {
throw new ClientException(
"Cannot create a CoreSession outside a security context, "
+ " login() missing.");
}
}
}
}
/**
* @deprecated since 5.9.3, use {@link CoreSession#close} instead.
*/
@Deprecated
public void close(CoreSession session) {
session.close(); // calls back closeCoreSession
}
/**
* Gets the number of open sessions.
*
* @since 5.4.2
*/
public int getNumberOfSessions() {
return sessions.size();
}
public Collection<RegistrationInfo> getRegistrationInfos() {
return sessions.values();
}
public Collection<RegistrationInfo> getRegistrationInfosLive(final boolean onThread) {
return Collections2.filter(sessions.values(), new Predicate<RegistrationInfo>() {
@Override
public boolean apply(RegistrationInfo input) {
return input.session.isLive(onThread);
}
});
}
public void cleanupThisThread() throws ClientException {
ClientException errors = new ClientException("disconnecting from storage for you");
for (RegistrationInfo each:CoreInstance.getInstance().getRegistrationInfosLive(true)) {
each.session.destroy();
errors.addSuppressed(each);
}
if (errors.getSuppressed().length > 0) {
throw errors;
}
}
}