001/*
002 *  Copyright 2012 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.repository.jcr;
017
018import javax.jcr.Node;
019import javax.jcr.PathNotFoundException;
020import javax.jcr.RepositoryException;
021import javax.jcr.Value;
022import javax.jcr.lock.Lock;
023import javax.jcr.lock.LockManager;
024
025import org.apache.avalon.framework.component.Component;
026import org.apache.avalon.framework.logger.AbstractLogEnabled;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030
031import org.ametys.core.user.CurrentUserProvider;
032import org.ametys.core.user.UserIdentity;
033import org.ametys.plugins.repository.AmetysObject;
034import org.ametys.plugins.repository.AmetysRepositoryException;
035import org.ametys.plugins.repository.RepositoryConstants;
036import org.ametys.plugins.repository.lock.UnlockHelper;
037
038/**
039 * Component that provides methods for lock management on {@link JCRAmetysObject}s.
040 */
041public class LockComponent extends AbstractLogEnabled implements Serviceable, Component
042{
043    
044    /** The avalon component role. */
045    public static final String ROLE = LockComponent.class.getName();
046    
047    /** The unlock helper. */
048    protected UnlockHelper _unlockHelper;
049    
050    /** The current user provider. */
051    protected CurrentUserProvider _currentUserProvider;
052    
053    @Override
054    public void service(ServiceManager manager) throws ServiceException
055    {
056        _unlockHelper = (UnlockHelper) manager.lookup(UnlockHelper.ROLE);
057        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
058    }
059    
060    /**
061     * Register a locked content for automatic unlocking
062     * @param object the locked {@link AmetysObject}
063     */
064    public void addLockedContent(AmetysObject object)
065    {
066        _unlockHelper.scheduleUnlocking(object);
067    }
068    
069    /**
070     * Unregister a locked content for automatic unlocking
071     * @param object the locked {@link AmetysObject}
072     */
073    public void removeLockedContent(AmetysObject object)
074    {
075        _unlockHelper.cancelUnlocking(object);
076    }
077    
078    /**
079     * Provides the current user.
080     * @return the current user.
081     */
082    public UserIdentity getCurrentUser()
083    {
084        return _currentUserProvider.getUser();
085    }
086    
087    /**
088     * Lock a {@link JCRAmetysObject}.
089     * @param object the object to lock.
090     * @throws AmetysRepositoryException if an error occurs.
091     */
092    public void lock(JCRAmetysObject object) throws AmetysRepositoryException
093    {
094        try
095        {
096            Node node = getNode(object);
097            
098            if (!node.isCheckedOut())
099            {
100                throw new AmetysRepositoryException("Unable to lock a checked-in Node");
101            }
102            
103            LockManager lockManager = node.getSession().getWorkspace().getLockManager();
104            
105            Lock lock = lockManager.lock(node.getPath(), false, false, Long.MAX_VALUE, null);
106            node.setProperty(RepositoryConstants.METADATA_LOCKTOKEN, lock.getLockToken());
107            
108            UserIdentity currentUser = getCurrentUser();
109            if (currentUser != null)
110            {
111                node.setProperty(RepositoryConstants.METADATA_LOCKOWNER, UserIdentity.userIdentityToString(currentUser));
112            }
113            node.getSession().save();
114
115            // On détache immédiatement le lock de la session, pour permettre
116            // les utilisations concurrentes
117            lockManager.removeLockToken(lock.getLockToken());
118
119            addLockedContent(object);
120        }
121        catch (RepositoryException ex)
122        {
123            throw new AmetysRepositoryException("Unable to lock object " + object, ex);
124        }
125    }
126    
127    /**
128     * Unlock a {@link JCRAmetysObject}.
129     * @param object the object to unlock.
130     * @throws AmetysRepositoryException if an error occurs.
131     */
132    public void unlock(JCRAmetysObject object) throws AmetysRepositoryException
133    {
134        try
135        {
136            Node node = getNode(object);
137            
138            String lockToken = node.getProperty(RepositoryConstants.METADATA_LOCKTOKEN).getString();
139            
140            LockManager lockManager = node.getSession().getWorkspace().getLockManager();
141
142            lockManager.addLockToken(lockToken);
143
144            lockManager.unlock(node.getPath());
145            
146            // Remove residual properties
147            node.setProperty(RepositoryConstants.METADATA_LOCKTOKEN, (Value) null);
148            node.setProperty(RepositoryConstants.METADATA_LOCKOWNER, (Value) null);
149            node.getSession().save();
150            
151            removeLockedContent(object);
152        }
153        catch (RepositoryException ex)
154        {
155            throw new AmetysRepositoryException("Unable to unlock content", ex);
156        }
157    }
158    
159    /**
160     * Test if a {@link JCRAmetysObject} is locked.
161     * @param object the object to test.
162     * @return true if the object is locked, false otherwise.
163     * @throws AmetysRepositoryException if an error occurs.
164     */
165    public boolean isLocked(JCRAmetysObject object) throws AmetysRepositoryException
166    {
167        try
168        {
169            return getNode(object).isLocked();
170        }
171        catch (RepositoryException ex)
172        {
173            throw new AmetysRepositoryException("Unable to get lock status on content", ex);
174        }
175    }
176    
177    /**
178     * Get the lock owner of a {@link JCRAmetysObject}.
179     * @param object the object of which to get the lock owner.
180     * @return the lock owner or null if there is no lock owner.
181     * @throws AmetysRepositoryException if an error occurs.
182     */
183    public UserIdentity getLockOwner(JCRAmetysObject object) throws AmetysRepositoryException
184    {
185        try
186        {
187            String userIdentity = getNode(object).getProperty(RepositoryConstants.METADATA_LOCKOWNER).getString();
188            return UserIdentity.stringToUserIdentity(userIdentity);
189        }
190        catch (PathNotFoundException e)
191        {
192            // No lock owner
193            return null;
194        }
195        catch (RepositoryException ex)
196        {
197            throw new AmetysRepositoryException("Unable to get lock owner on content", ex);
198        }
199    }
200    
201    /**
202     * Get an object's base node.
203     * @param object the object which node to get.
204     * @return the object's base node.
205     */
206    protected Node getNode(JCRAmetysObject object)
207    {
208        Node node = null;
209        
210        if (object instanceof DefaultAmetysObject)
211        {
212            node = ((DefaultAmetysObject) object).getBaseNode();
213        }
214        else
215        {
216            node = object.getNode();
217        }
218        
219        return node;
220    }
221    
222}