001/*
002 * Copyright (c) 2006-2011 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 *     bstefanescu
011 */
012package org.nuxeo.ecm.automation;
013
014import java.util.ArrayList;
015import java.util.List;
016
017import javax.security.auth.login.LoginContext;
018import javax.security.auth.login.LoginException;
019
020import org.nuxeo.ecm.core.api.CoreInstance;
021import org.nuxeo.ecm.core.api.CoreSession;
022
023/**
024 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
025 */
026public class LoginStack {
027
028    protected List<Entry> stack = new ArrayList<Entry>();
029
030    protected CoreSession originalSession;
031
032    protected CoreSession currentSession;
033
034    public LoginStack(CoreSession session) {
035        setSession(session);
036    }
037
038    public void setSession(CoreSession session) {
039        originalSession = session;
040        currentSession = session;
041    }
042
043    /**
044     * Get the current session
045     *
046     * @return
047     */
048    public CoreSession getSession() {
049        return currentSession;
050    }
051
052    public void push(LoginContext lc) {
053        Entry entry = new Entry(lc);
054        String repositoryName;
055        if (originalSession != null) {
056            repositoryName = originalSession.getRepositoryName();
057        } else {
058            repositoryName = null; // default repository
059        }
060        entry.session = CoreInstance.openCoreSession(repositoryName);
061        currentSession = entry.session;
062        stack.add(entry);
063    }
064
065    public Entry peek() {
066        if (!stack.isEmpty()) {
067            return stack.get(stack.size() - 1);
068        }
069        return null;
070    }
071
072    /**
073     * Remove the current login context from the stack.
074     * <p>
075     * If no login context in in the stack nothing is done. If the login context has an associated CoreSession the
076     * session will be destroyed and the previous session is restored as the active session of the operation context.
077     */
078    public void pop() throws OperationException {
079        if (!stack.isEmpty()) {
080            Entry entry = stack.remove(stack.size() - 1);
081            entry.dispose();
082            entry = peek();
083            if (entry != null) {
084                currentSession = entry.session;
085            } else {
086                currentSession = originalSession;
087            }
088            if (currentSession != null) {
089                refreshSession(currentSession);
090            }
091        }
092    }
093
094    protected void refreshSession(CoreSession session) throws OperationException {
095        if (session != null && !session.isStateSharedByAllThreadSessions()) {
096            // this will indirectly process refresh the session
097            session.save();
098        }
099    }
100
101    /**
102     * Remove the stacked logins if any. This is called when chain execution is done.
103     */
104    protected void clear() throws OperationException {
105        if (!stack.isEmpty()) {
106            for (int i = stack.size() - 1; i > -1; i--) {
107                stack.get(i).dispose();
108            }
109            stack.clear();
110            currentSession = originalSession;
111            if (currentSession != null) {
112                refreshSession(currentSession);
113            }
114            stack.clear();
115        }
116    }
117
118    public static class Entry {
119        public LoginContext lc;
120
121        public CoreSession session;
122
123        public Entry(LoginContext lc) {
124            this(lc, null);
125        }
126
127        public Entry(LoginContext lc, CoreSession session) {
128            this.lc = lc;
129            this.session = session;
130        }
131
132        public final boolean hasSession() {
133            return session != null;
134        }
135
136        public final void dispose() throws OperationException {
137            try {
138                if (session != null) {
139                    try {
140                        session.save();
141                    } finally {
142                        session.close();
143                    }
144                }
145            } finally {
146                try {
147                    session = null;
148                    lc.logout();
149                    lc = null;
150                } catch (LoginException e) {
151                    throw new OperationException(e);
152                }
153            }
154        }
155    }
156
157}