View Javadoc
1   /*
2    * Copyright (C) 2003-2008 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.cms.lock.impl;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import javax.jcr.Node;
27  import javax.jcr.NodeIterator;
28  import javax.jcr.PathNotFoundException;
29  import javax.jcr.RepositoryException;
30  import javax.jcr.Session;
31  import javax.jcr.query.Query;
32  import javax.jcr.query.QueryResult;
33  
34  import org.exoplatform.container.component.ComponentPlugin;
35  import org.exoplatform.container.xml.InitParams;
36  import org.exoplatform.services.cache.CacheService;
37  import org.exoplatform.services.cache.ExoCache;
38  import org.exoplatform.services.cms.lock.LockService;
39  import org.exoplatform.services.jcr.RepositoryService;
40  import org.exoplatform.services.jcr.core.ManageableRepository;
41  import org.exoplatform.services.jcr.ext.common.SessionProvider;
42  import org.exoplatform.services.log.ExoLogger;
43  import org.exoplatform.services.log.Log;
44  import org.exoplatform.services.security.ConversationState;
45  import org.exoplatform.services.security.Identity;
46  import org.exoplatform.services.security.IdentityConstants;
47  import org.exoplatform.services.security.MembershipEntry;
48  import org.exoplatform.services.wcm.core.NodetypeConstant;
49  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
50  import org.picocontainer.Startable;
51  
52  /**
53   * Created by The eXo Platform SAS
54   * Author : Chien Nguyen
55   * chien.nguyen@exoplatform.com
56   * Nov 17, 2009
57   */
58  
59  public class LockServiceImpl implements LockService, Startable {
60  
61    private final String SETTING_LOCK="SETTING_LOCK";
62    private final String PRE_SETTING_LOCK="PRE_SETTING_LOCK";
63    private final String CACHE_NAME = "ecms.LockService";
64  
65    private ExoCache<String, List<String>> settingLockList;
66    private List<LockGroupsOrUsersPlugin> lockGroupsOrUsersPlugin_ = new ArrayList<LockGroupsOrUsersPlugin>();
67    private static final Log LOG = ExoLogger.getLogger(LockServiceImpl.class.getName());
68    private HashMap<String, Map<String, String>> lockHolding = new HashMap<String, Map<String, String>>();
69  
70    /**
71     * Constructor method
72     * @param params
73     * @throws Exception
74     */
75    public LockServiceImpl(InitParams params, CacheService cacheService) throws Exception {
76      //group_ = params.getValueParam("group").getValue();
77      settingLockList = cacheService.getCacheInstance(CACHE_NAME);
78    }
79  
80    /**
81     * Add new users or groups into lockGroupsOrUsersPlugin_
82     * @param plugin
83     */
84    public void addLockGroupsOrUsersPlugin(ComponentPlugin plugin) {
85      if (plugin instanceof LockGroupsOrUsersPlugin)
86        lockGroupsOrUsersPlugin_.add((LockGroupsOrUsersPlugin)plugin);
87    }
88  
89    /**
90     * {@inheritDoc}
91     */
92    @Override
93    public List<String> getPreSettingLockList(){
94      return settingLockList.get(PRE_SETTING_LOCK);
95    }
96  
97    /**
98     * {@inheritDoc}
99     */
100   public List<String> getAllGroupsOrUsersForLock() throws Exception {
101     return settingLockList.get(SETTING_LOCK);
102   }
103 
104   /**
105    * {@inheritDoc}
106    */
107   public void addGroupsOrUsersForLock(String groupsOrUsers) throws Exception {
108     List<String> _settingLockList = settingLockList.get(SETTING_LOCK);
109     if (_settingLockList!=null && !_settingLockList.contains(groupsOrUsers)) {
110       _settingLockList.add(groupsOrUsers);
111       settingLockList.put(SETTING_LOCK, _settingLockList);
112     }
113   }
114 
115   /**
116    * {@inheritDoc}
117    */
118   public void removeGroupsOrUsersForLock(String groupsOrUsers) throws Exception {
119     List<String> _settingLockList = settingLockList.get(SETTING_LOCK);
120     if (_settingLockList!=null && _settingLockList.contains(groupsOrUsers)) {
121       _settingLockList.remove(groupsOrUsers);
122       settingLockList.put(SETTING_LOCK, _settingLockList);
123     }
124   }
125 
126   /**
127    * {@inheritDoc}
128    */
129   public void start() {
130     lockHolding.clear();
131     settingLockList.clearCache();
132 
133     removeLocks();
134     List<String> _settingLockList = new ArrayList<String>();
135     List<String> _preSettingLockList = new ArrayList<String>();
136     for(LockGroupsOrUsersPlugin plugin : lockGroupsOrUsersPlugin_) {
137       _settingLockList.addAll(plugin.initGroupsOrUsers());
138       _preSettingLockList.addAll(plugin.initGroupsOrUsers());
139     }
140     settingLockList.put(SETTING_LOCK, _settingLockList);
141     settingLockList.put(PRE_SETTING_LOCK, _preSettingLockList);
142   }
143 
144   /**
145    * {@inheritDoc}
146    */
147   public void stop() {
148     lockHolding.clear();
149     settingLockList.clearCache();
150   }
151 
152   /**
153    * {@inheritDoc}
154    */
155   @Override
156   public HashMap<String, Map<String, String>> getLockHolding() {
157     return lockHolding;
158   }
159 
160   /**
161    * {@inheritDoc}
162    */
163   @Override
164   public void putToLockHoding(String userId, Map<String, String> lockedNodesInfo) {
165     lockHolding.put(userId, lockedNodesInfo);
166   }
167 
168   /**
169    * {@inheritDoc}
170    */
171   @Override
172   public Map<String, String> getLockInformation(String userId) {
173     return lockHolding.get(userId);
174   }
175 
176   /**
177    * {@inheritDoc}
178    */
179   @Override
180   public void removeLocksOfUser(String userId) {
181     SessionProvider sessionProvider = SessionProvider.createSystemProvider();
182     RepositoryService repositoryService = WCMCoreUtils.getService(RepositoryService.class);
183     if (LOG.isInfoEnabled()) {
184       LOG.info("Removing all locked nodes of user " + userId);
185     }
186     Map<String,String> lockedNodes = lockHolding.get(userId);
187     if(lockedNodes == null || lockedNodes.values().isEmpty()) return;
188     try {
189       for(Iterator<String> iter = lockedNodes.keySet().iterator(); iter.hasNext();) {
190         try {
191           //The key structure is built in org.exoplatform.ecm.webui.utils.LockUtil.createLockKey() method
192           String key = iter.next();
193           String[] temp = key.split(":/:");
194           String nodePath = temp[1];
195           String[] location = temp[0].split("/::/");
196           String workspaceName = location[1] ;
197           Session session = sessionProvider.getSession(workspaceName, repositoryService.getCurrentRepository());
198           String lockToken = lockedNodes.get(key);
199           session.addLockToken(lockToken);
200           Node node =null;
201           try {
202             node = (Node)session.getItem(nodePath);
203           }catch (PathNotFoundException e) {
204             if (LOG.isInfoEnabled()) {
205               LOG.info("Node " + nodePath + " has been already removed before");
206             }
207             continue;
208           }
209           if (!node.isCheckedOut() && node.isNodeType(NodetypeConstant.MIX_VERSIONABLE)) {
210             node.checkout();
211           }
212           if (node.isLocked()) {
213             node.unlock();
214           }
215           if (node.isNodeType("mix:lockable") && node.isCheckedOut()) {
216             node.removeMixin("mix:lockable");
217           }
218           node.save();
219         } catch (Exception e) {
220           if (LOG.isErrorEnabled()) {
221             LOG.error("Error while unlocking the locked nodes",e);
222           }
223         }
224       }
225       lockedNodes.clear();
226     } finally {
227       sessionProvider.close();
228     }
229   }
230 
231   /**
232    * {@inheritDoc}
233    */
234   @Override
235   public void removeLocks() {
236     if (LOG.isInfoEnabled()) {
237       LOG.info("Clean all locked nodes in the system");
238     }
239     SessionProvider sessionProvider = SessionProvider.createSystemProvider();
240     RepositoryService repositoryService = WCMCoreUtils.getService(RepositoryService.class);
241     try {
242       String wsName = repositoryService.getCurrentRepository().getConfiguration().getDefaultWorkspaceName();
243       Session session = sessionProvider.getSession(wsName, repositoryService.getCurrentRepository());
244       String lockQueryStatement = "SELECT * from mix:lockable ORDER BY exo:dateCreated";
245       QueryResult queryResult = session.getWorkspace().getQueryManager().createQuery(lockQueryStatement, Query.SQL).execute();
246       NodeIterator nodeIter = queryResult.getNodes();
247       while(nodeIter.hasNext()) {
248         Node lockedNode = nodeIter.nextNode();
249         //Check to avoid contains some corrupted data in the system which still contains mix:lockable but not locked.
250         if (!lockedNode.isCheckedOut() && lockedNode.isNodeType(NodetypeConstant.MIX_VERSIONABLE)) {
251           lockedNode.checkout();
252         }
253         if(lockedNode.isLocked()) {
254           lockedNode.unlock();
255         }
256         if (lockedNode.isNodeType("mix:lockable") && lockedNode.isCheckedOut()) {
257           lockedNode.removeMixin("mix:lockable");
258         }
259         lockedNode.save();
260       }
261     } catch(RepositoryException re) {
262       if (LOG.isErrorEnabled()) {
263         LOG.error("Error while unlocking the locked nodes", re);
264       }
265     } finally {
266       sessionProvider.close();
267     }
268   }
269 
270   /**
271    * {@inheritDoc}
272    */
273   @Override
274   public String getLockTokenOfUser(Node node) throws Exception {
275     String key = createLockKey(node);
276     String userId = ConversationState.getCurrent().getIdentity().getUserId();
277     if(userId == null) userId = IdentityConstants.ANONIM;
278     Map<String,String> lockedNodesInfo = getLockInformation(userId);
279     if ((lockedNodesInfo != null) && (lockedNodesInfo.get(key) != null)) {
280       return lockedNodesInfo.get(key);
281     }
282     return null;
283   }
284 
285   /**
286    * {@inheritDoc}
287    */
288   @Override
289   public String createLockKey(Node node) throws Exception {
290     StringBuffer buffer = new StringBuffer();
291     Session session = node.getSession();
292     String repositoryName = ((ManageableRepository)session.getRepository()).getConfiguration().getName();
293     String userId = ConversationState.getCurrent().getIdentity().getUserId();
294     if(userId == null) userId = IdentityConstants.ANONIM;
295     buffer.append(repositoryName).append("/::/")
296           .append(session.getWorkspace().getName()).append("/::/")
297           .append(userId).append(":/:")
298           .append(node.getPath());
299     return buffer.toString();
300   }
301 
302   /**
303    * {@inheritDoc}
304    */
305   @Override
306   public String createLockKey(Node node, String userId) throws Exception {
307     StringBuffer buffer = new StringBuffer();
308     Session session = node.getSession();
309     String repositoryName = ((ManageableRepository)session.getRepository()).getConfiguration().getName();
310     if(userId == null) userId = IdentityConstants.ANONIM;
311     buffer.append(repositoryName).append("/::/")
312           .append(session.getWorkspace().getName()).append("/::/")
313           .append(userId).append(":/:")
314           .append(node.getPath());
315     return buffer.toString();
316   }
317 
318   /**
319    * {@inheritDoc}
320    */
321   @Override
322   public String getLockToken(Node node) throws Exception {
323     String key = createLockKey(node);
324     Identity currentIdentity = ConversationState.getCurrent().getIdentity();
325     String userId = currentIdentity.getUserId();
326     if(userId == null) userId = IdentityConstants.ANONIM;
327     Map<String,String> lockedNodesInfo = getLockInformation(userId);
328     if ((lockedNodesInfo != null) && (lockedNodesInfo.get(key) != null)) {
329       return lockedNodesInfo.get(key);
330     }
331 
332     Collection<MembershipEntry> collection = currentIdentity.getMemberships();
333     String keyPermission;
334     for(MembershipEntry membership : collection) {
335       StringBuffer permissionBuffer = new StringBuffer();
336       permissionBuffer.append(membership.getMembershipType()).append(":").append(membership.getGroup());
337       if ((permissionBuffer != null) && (permissionBuffer.toString().length() > 0)) {
338         keyPermission = createLockKey(node, permissionBuffer.toString());
339         lockedNodesInfo = getLockInformation(permissionBuffer.toString());
340         if ((lockedNodesInfo != null) && (lockedNodesInfo.get(keyPermission) != null)) {
341           return lockedNodesInfo.get(keyPermission);
342         }
343       }
344     }
345     return null;
346   }
347 
348   /**
349    * {@inheritDoc}
350    */
351   @Override
352   public void changeLockToken(String srcPath, Node newNode) throws Exception {
353     String newKey = createLockKey(newNode);
354     String oldKey = getOldLockKey(srcPath, newNode);
355     String userId = ConversationState.getCurrent().getIdentity().getUserId();
356     if(userId == null) userId = IdentityConstants.ANONIM;
357     Map<String,String> lockedNodesInfo = getLockInformation(userId);
358     if(lockedNodesInfo == null) {
359       lockedNodesInfo = new HashMap<String,String>();
360     }
361     if(lockedNodesInfo.containsKey(oldKey)) {
362       lockedNodesInfo.put(newKey, lockedNodesInfo.get(oldKey));
363       lockedNodesInfo.remove(oldKey);
364     }
365     putToLockHoding(userId, lockedNodesInfo);
366   }
367 
368   /**
369    * {@inheritDoc}
370    */
371   @Override
372   public void changeLockToken(Node oldNode, Node newNode) throws Exception {
373     String newKey = createLockKey(newNode);
374     String oldKey = createLockKey(oldNode);
375     String userId = ConversationState.getCurrent().getIdentity().getUserId();
376     if(userId == null) userId = IdentityConstants.ANONIM;
377     Map<String,String> lockedNodesInfo = getLockInformation(userId);
378     if(lockedNodesInfo == null) {
379       lockedNodesInfo = new HashMap<String,String>();
380     }
381     lockedNodesInfo.remove(oldKey) ;
382     lockedNodesInfo.put(newKey,newNode.getLock().getLockToken());
383     putToLockHoding(userId,lockedNodesInfo);
384   }
385 
386   /**
387    * {@inheritDoc}
388    */
389   @Override
390   public String getOldLockKey(String srcPath, Node node) throws Exception {
391     StringBuffer buffer = new StringBuffer();
392     Session session = node.getSession();
393     String repositoryName = ((ManageableRepository)session.getRepository()).getConfiguration().getName();
394     buffer.append(repositoryName).append("/::/")
395           .append(session.getWorkspace().getName()).append("/::/")
396           .append(session.getUserID()).append(":/:")
397           .append(srcPath);
398     return buffer.toString();
399   }
400 }