001/*
002 *  Copyright 2011 Anyware Services
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 */
016package org.ametys.plugins.repositoryapp;
017
018import java.util.HashMap;
019import java.util.Map;
020
021import javax.jcr.Credentials;
022import javax.jcr.Repository;
023import javax.jcr.RepositoryException;
024import javax.jcr.Session;
025
026import org.apache.avalon.framework.component.Component;
027import org.apache.avalon.framework.context.ContextException;
028import org.apache.avalon.framework.context.Contextualizable;
029import org.apache.avalon.framework.logger.AbstractLogEnabled;
030import org.apache.cocoon.Constants;
031import org.apache.cocoon.components.ContextHelper;
032import org.apache.cocoon.environment.Context;
033import org.apache.jackrabbit.api.JackrabbitRepository;
034
035/**
036 * Repository helper.
037 */
038public class RepositoryProvider extends AbstractLogEnabled implements Contextualizable, Component
039{
040    /** The avalon role. */
041    public static final String ROLE = RepositoryProvider.class.getName();
042    
043    /** The session map key. */
044    public static final String SESSION_JCR_SESSIONS_KEY = "org.ametys.repository.Sessions";
045    
046    /** The context repository key. */
047    public static final String CONTEXT_REPOSITORY_KEY = "org.ametys.repository.Repository";
048    
049    /** The JNDI variable key. */
050    public static final String CONTEXT_IS_JNDI_KEY = "org.ametys.repository.JNDI";
051    
052    /** The credentials object key. */
053    private static final String _CONTEXT_CREDENTIALS_KEY = "org.ametys.repository.Credentials";
054    
055    /** The avalon context, initialized during the contextualize method */
056    private org.apache.avalon.framework.context.Context _avalonContext;
057    
058    private Context _context;
059    
060    @Override
061    public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException
062    {
063        _avalonContext = context;
064        _context = (Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
065    }
066    
067    /**
068     * Get the repository.
069     * @return the repository.
070     */
071    public Repository getRepository()
072    {
073        return (Repository) _context.getAttribute(CONTEXT_REPOSITORY_KEY);
074    }
075    
076    /**
077     * Get the credentials.
078     * @return the credentials.
079     */
080    public Credentials getCredentials()
081    {
082        return (Credentials) _context.getAttribute(_CONTEXT_CREDENTIALS_KEY);
083    }
084    
085    /**
086     * Determines if the repository is obtained through JNDI
087     * @return true if the repository is obtained through JNDI
088     */
089    public boolean isJndi()
090    {
091        return (Boolean) _context.getAttribute(CONTEXT_IS_JNDI_KEY);
092    }
093    
094    /**
095     * Register a repository.
096     * @param repository The repository to register
097     * @param credentials The credentials to connect
098     * @param jndi True to connect using jndi
099     */
100    public void registerRepository(Repository repository, Credentials credentials, boolean jndi)
101    {
102        _context.setAttribute(CONTEXT_REPOSITORY_KEY, repository);
103        _context.setAttribute(_CONTEXT_CREDENTIALS_KEY, credentials);
104        _context.setAttribute(CONTEXT_IS_JNDI_KEY, jndi);
105    }
106    
107    /**
108     * Get the JCR session map.
109     * @return a Map of the JCR sessions, indexed by workspace name.
110     */
111    @SuppressWarnings("unchecked")
112    public Map<String, Session> getSessions()
113    {
114        org.apache.cocoon.environment.Session httpSession = ContextHelper.getRequest(_avalonContext).getSession();
115        
116        Map<String, Session> sessions = (Map<String, Session>) httpSession.getAttribute(SESSION_JCR_SESSIONS_KEY);
117        if (sessions == null)
118        {
119            sessions = new HashMap<>();
120            httpSession.setAttribute(SESSION_JCR_SESSIONS_KEY, sessions);
121        }
122        
123        return sessions;
124    }
125    
126    /**
127     * Get a session on the given workspace.
128     * @param workspaceName the workspace name.
129     * @return the JCR session.
130     * @throws RepositoryException if an error occurred
131     */
132    public Session getSession(String workspaceName) throws RepositoryException
133    {
134        Map<String, Session> sessions = getSessions();
135        
136        Session session = sessions.get(workspaceName);
137        
138        if (session == null || !session.isLive())
139        {
140            Repository repository = getRepository();
141            Credentials credentials = getCredentials();
142            
143            session = repository.login(credentials, workspaceName);
144            sessions.put(workspaceName, session);
145        }
146        
147        return session;
148    }
149    
150    /**
151     * Disconnect from the repository.
152     */
153    public void disconnect()
154    {
155        // Logout all the sessions.
156        Map<String, Session> sessions = getSessions();
157        for (Session session : sessions.values())
158        {
159            session.logout();
160        }
161        
162        org.apache.cocoon.environment.Session httpSession = ContextHelper.getRequest(_avalonContext).getSession();
163        httpSession.removeAttribute(SESSION_JCR_SESSIONS_KEY);
164        
165        // We should test if the component manager knows the repository (embedded mode),
166        // and don't call shutdown in that case, but disconnect is called by LogoutAction, which is
167        // only present in standalone mode, so we should be fine.
168        Repository repository = getRepository();
169        if (!isJndi() && repository instanceof JackrabbitRepository)
170        {
171            ((JackrabbitRepository) repository).shutdown();
172        }
173        
174        // Destroy the connection information.
175        _context.removeAttribute(CONTEXT_REPOSITORY_KEY);
176        _context.removeAttribute(_CONTEXT_CREDENTIALS_KEY);
177        _context.removeAttribute(CONTEXT_IS_JNDI_KEY);
178    }
179}