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