001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $ 018 */ 019 020package org.nuxeo.ecm.webapp.documentsLists; 021 022import java.io.Serializable; 023import java.security.MessageDigest; 024import java.security.NoSuchAlgorithmException; 025import java.util.ArrayList; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.nuxeo.common.utils.Base64; 033import org.nuxeo.ecm.core.api.CoreSession; 034import org.nuxeo.ecm.core.api.DocumentModel; 035import org.nuxeo.ecm.core.api.DocumentModelList; 036import org.nuxeo.ecm.core.api.DocumentNotFoundException; 037import org.nuxeo.ecm.core.api.DocumentRef; 038import org.nuxeo.ecm.core.api.IdRef; 039import org.nuxeo.ecm.core.api.PathRef; 040import org.nuxeo.ecm.directory.DirectoryException; 041import org.nuxeo.ecm.directory.Session; 042import org.nuxeo.ecm.directory.api.DirectoryService; 043import org.nuxeo.ecm.platform.ui.web.directory.DirectoryHelper; 044 045/** 046 * Manage DocumentsLists Persistence. Uses a SQL Directory as storage Backend. 047 * 048 * @author tiry 049 */ 050public class DocumentsListsPersistenceManager { 051 052 private static final String DIR_NAME = "documentsLists"; 053 054 private static final String ID_SEP = ":"; 055 056 private static final String DIR_COL_USERID = "userid"; 057 058 private static final String DIR_COL_LISTID = "listid"; 059 060 private static final String DIR_COL_REF = "ref"; 061 062 private static final String DIR_COL_REFTYPE = "reftype"; 063 064 private static final String DIR_COL_REPO = "repo"; 065 066 // Hey, you never know ! 067 private static final boolean ENABLE_SANITY_CHECK = true; 068 069 private static final boolean FIX_SANITY_ERROR = true; 070 071 private static final Log log = LogFactory.getLog(DocumentsListsPersistenceManager.class); 072 073 private DirectoryService directoryService; 074 075 private Session dirSession; 076 077 private String directorySchema; 078 079 private static String getIdForEntry(String userName, String listName, DocumentModel doc) { 080 String ref = doc.getRef().toString(); 081 int refType = doc.getRef().type(); 082 String repoId = doc.getRepositoryName(); 083 084 return getIdForEntry(userName, listName, ref, refType, repoId); 085 } 086 087 private static String getIdForEntry(String userName, String listName, String ref, int refType, String repoId) { 088 StringBuilder sb = new StringBuilder(); 089 sb.append(listName); 090 sb.append(ID_SEP); 091 sb.append(userName); 092 sb.append(ID_SEP); 093 sb.append(refType); 094 sb.append(ID_SEP); 095 sb.append(ref); 096 sb.append(ID_SEP); 097 sb.append(repoId); 098 099 byte[] idDigest; 100 try { 101 idDigest = MessageDigest.getInstance("MD5").digest(sb.toString().getBytes()); 102 } catch (NoSuchAlgorithmException e) { 103 // should never append 104 return sb.toString(); 105 } 106 return Base64.encodeBytes(idDigest); 107 } 108 109 private boolean initPersistentService() { 110 if (dirSession != null) { 111 return true; 112 } 113 114 if (directoryService == null) { 115 directoryService = DirectoryHelper.getDirectoryService(); 116 if (directoryService == null) { 117 return false; 118 } 119 } 120 121 try { 122 dirSession = directoryService.open(DIR_NAME); 123 directorySchema = directoryService.getDirectorySchema(DIR_NAME); 124 } catch (DirectoryException e) { 125 dirSession = null; 126 log.error("Unable to open directory " + DIR_NAME + " : " + e.getMessage()); 127 return false; 128 } 129 return true; 130 } 131 132 private void releasePersistenceService() { 133 // for now directory sessions are lost during passivation of the DirectoryFacade 134 // this can't be tested on the client side 135 // => release directorySession after each call ... 136 137 if (directoryService == null) { 138 dirSession = null; 139 return; 140 } 141 if (dirSession != null) { 142 try { 143 dirSession.close(); 144 } catch (DirectoryException e) { 145 // do nothing 146 } 147 } 148 dirSession = null; 149 } 150 151 private static DocumentModel getDocModel(CoreSession session, String ref, long refType, String repoId) { 152 153 if (!session.getRepositoryName().equals(repoId)) { 154 log.error("Multiple repository management is not handled in current implementation"); 155 return null; 156 } 157 DocumentRef docRef; 158 if (refType == DocumentRef.ID) { 159 docRef = new IdRef(ref); 160 } else if (refType == DocumentRef.PATH) { 161 docRef = new PathRef(ref); 162 } else { 163 log.error("Unknown reference type"); 164 return null; 165 } 166 167 DocumentModel doc = null; 168 try { 169 doc = session.getDocument(docRef); 170 } catch (DocumentNotFoundException e) { 171 log.warn("document with ref " + ref + " was not found : " + e.getMessage()); 172 return null; 173 } 174 175 return doc; 176 } 177 178 public List<DocumentModel> loadPersistentDocumentsLists(CoreSession currentSession, String userName, String listName) { 179 List<DocumentModel> docList = new ArrayList<DocumentModel>(); 180 if (!initPersistentService()) { 181 return docList; 182 } 183 try { 184 Map<String, Serializable> filter = new HashMap<String, Serializable>(); 185 filter.put(DIR_COL_LISTID, listName); 186 filter.put(DIR_COL_USERID, userName); 187 188 DocumentModelList entries; 189 try { 190 entries = dirSession.query(filter); 191 } catch (DirectoryException e) { 192 log.error(e, e); 193 return docList; 194 } 195 196 for (DocumentModel entry : entries) { 197 String ref = (String) entry.getProperty(directorySchema, DIR_COL_REF); 198 long reftype = (Long) entry.getProperty(directorySchema, DIR_COL_REFTYPE); 199 String repo = (String) entry.getProperty(directorySchema, DIR_COL_REPO); 200 201 DocumentModel doc = getDocModel(currentSession, ref, reftype, repo); 202 203 if (doc != null) { 204 if (ENABLE_SANITY_CHECK) { 205 if (docList.contains(doc)) { 206 log.warn("Document " + doc.getRef().toString() + " is duplicated in persistent list " 207 + listName); 208 if (FIX_SANITY_ERROR) { 209 try { 210 dirSession.deleteEntry(entry.getId()); 211 } catch (DirectoryException e) { 212 log.error("Sanity fix failed", e); 213 } 214 } 215 } else { 216 docList.add(doc); 217 } 218 } else { 219 docList.add(doc); 220 } 221 } else { 222 // not found => do the remove 223 try { 224 dirSession.deleteEntry(entry.getId()); 225 } catch (DirectoryException e) { 226 log.error("Unable to remove non existing document model entry : " + entry.getId(), e); 227 } 228 } 229 } 230 return docList; 231 } finally { 232 releasePersistenceService(); 233 } 234 } 235 236 public Boolean addDocumentToPersistentList(String userName, String listName, DocumentModel doc) { 237 if (!initPersistentService()) { 238 return false; 239 } 240 try { 241 Map<String, Object> fields = new HashMap<String, Object>(); 242 fields.put(DIR_COL_LISTID, listName); 243 fields.put(DIR_COL_USERID, userName); 244 fields.put(DIR_COL_REF, doc.getRef().toString()); 245 fields.put(DIR_COL_REFTYPE, (long) doc.getRef().type()); 246 fields.put(DIR_COL_REPO, doc.getRepositoryName()); 247 String id = getIdForEntry(userName, listName, doc); 248 fields.put("id", id); 249 try { 250 if (ENABLE_SANITY_CHECK) { 251 DocumentModel badEntry = dirSession.getEntry(id); 252 if (badEntry != null) { 253 log.warn("Entry with id " + id + " is already present : please check DB integrity"); 254 if (FIX_SANITY_ERROR) { 255 dirSession.deleteEntry(id); 256 } 257 } 258 } 259 dirSession.createEntry(fields); 260 } catch (DirectoryException e) { 261 log.error("Unable to create entry", e); 262 return false; 263 } 264 return true; 265 } finally { 266 releasePersistenceService(); 267 } 268 } 269 270 public Boolean removeDocumentFromPersistentList(String userName, String listName, DocumentModel doc) { 271 if (!initPersistentService()) { 272 return false; 273 } 274 try { 275 String entryId = getIdForEntry(userName, listName, doc); 276 try { 277 dirSession.deleteEntry(entryId); 278 } catch (DirectoryException e) { 279 log.error("Unable to delete entry", e); 280 return false; 281 } 282 return true; 283 } finally { 284 releasePersistenceService(); 285 } 286 } 287 288 public Boolean clearPersistentList(String userName, String listName) { 289 if (!initPersistentService()) { 290 return false; 291 } 292 try { 293 Map<String, Serializable> filter = new HashMap<String, Serializable>(); 294 filter.put(DIR_COL_LISTID, listName); 295 filter.put(DIR_COL_USERID, userName); 296 try { 297 DocumentModelList entriesToDelete = dirSession.query(filter); 298 for (DocumentModel entry : entriesToDelete) { 299 dirSession.deleteEntry(entry.getId()); 300 } 301 } catch (DirectoryException e) { 302 log.error("Unable to clear DocumentList", e); 303 return false; 304 } 305 return true; 306 } finally { 307 releasePersistenceService(); 308 } 309 } 310 311}