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