001/* 002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. 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-2.1.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 * Thomas Roger 016 */ 017 018package org.nuxeo.ecm.platform.picture; 019 020import static org.apache.commons.logging.LogFactory.getLog; 021import static org.nuxeo.ecm.core.api.CoreSession.ALLOW_VERSION_WRITE; 022import static org.nuxeo.ecm.core.query.sql.NXQL.ECM_UUID; 023import static org.nuxeo.ecm.core.query.sql.NXQL.NXQL; 024 025import java.io.Serializable; 026import java.util.HashSet; 027import java.util.Map; 028import java.util.Set; 029 030import org.apache.commons.logging.Log; 031import org.nuxeo.ecm.core.api.Blob; 032import org.nuxeo.ecm.core.api.CoreSession; 033import org.nuxeo.ecm.core.api.DocumentModel; 034import org.nuxeo.ecm.core.api.IdRef; 035import org.nuxeo.ecm.core.api.IterableQueryResult; 036import org.nuxeo.ecm.core.repository.RepositoryInitializationHandler; 037import org.nuxeo.ecm.platform.picture.api.PictureView; 038import org.nuxeo.ecm.platform.picture.api.adapters.MultiviewPicture; 039import org.nuxeo.runtime.transaction.TransactionHelper; 040 041/** 042 * Migrate old Original picture view to {@code file:content}. 043 * <p> 044 * Does not copy it if {@code file:content} is not empty. When done, the Original picture view is removed. 045 * <p> 046 * It does not recompute the picture views. 047 * 048 * @since 7.2 049 */ 050public class PictureMigrationHandler extends RepositoryInitializationHandler { 051 052 private static final Log log = getLog(PictureMigrationHandler.class); 053 054 public static final String PICTURES_TO_MIGRATE_QUERY = "SELECT ecm:uuid FROM Document " 055 + "WHERE ecm:mixinType = 'Picture' AND ecm:isProxy = 0 AND views/*/title = 'Original' " 056 + "AND content/data IS NULL"; 057 058 public static final String ORIGINAL_VIEW_TITLE = "Original"; 059 060 public static final String FILE_CONTENT_PROPERTY = "file:content"; 061 062 public static final String FILE_FILENAME_PROPERTY = "file:filename"; 063 064 public static final int BATCH_SIZE = 50; 065 066 public static final String DISABLE_QUOTA_CHECK_LISTENER = "disableQuotaListener"; 067 068 @Override 069 public void doInitializeRepository(CoreSession session) { 070 boolean txStarted = false; 071 if (!TransactionHelper.isTransactionActive()) { 072 txStarted = true; 073 } 074 075 try { 076 doMigration(session); 077 } finally { 078 if (txStarted) { 079 TransactionHelper.commitOrRollbackTransaction(); 080 } 081 } 082 } 083 084 protected void doMigration(CoreSession session) { 085 Set<String> pictureIds = getPictureIdsToMigrate(session); 086 if (pictureIds.isEmpty()) { 087 return; 088 } 089 090 if (log.isWarnEnabled()) { 091 log.warn(String.format("Started migration of %d documents with the 'Picture' facet", pictureIds.size())); 092 } 093 094 long pictureMigratedCount = 0; 095 try { 096 for (String pictureId : pictureIds) { 097 if (migratePicture(session, pictureId)) { 098 if (++pictureMigratedCount % BATCH_SIZE == 0) { 099 TransactionHelper.commitOrRollbackTransaction(); 100 TransactionHelper.startTransaction(); 101 } 102 } 103 } 104 } finally { 105 TransactionHelper.commitOrRollbackTransaction(); 106 TransactionHelper.startTransaction(); 107 } 108 109 if (log.isWarnEnabled()) { 110 log.warn(String.format("Finished migration of %d/%d documents with the 'Picture' facet", 111 pictureMigratedCount, pictureIds.size())); 112 } 113 } 114 115 protected Set<String> getPictureIdsToMigrate(CoreSession session) { 116 IterableQueryResult it = null; 117 Set<String> pictureIds = new HashSet<>(); 118 119 try { 120 it = session.queryAndFetch(PICTURES_TO_MIGRATE_QUERY, NXQL); 121 122 for (Map<String, Serializable> map : it) { 123 String id = (String) map.get(ECM_UUID); 124 if (id != null) { 125 pictureIds.add(id); 126 } 127 } 128 } finally { 129 if (it != null) { 130 it.close(); 131 } 132 } 133 return pictureIds; 134 } 135 136 protected boolean migratePicture(CoreSession session, String docId) { 137 DocumentModel picture = session.getDocument(new IdRef(docId)); 138 139 if (log.isDebugEnabled()) { 140 log.debug(String.format("Migrating %s", picture)); 141 } 142 143 MultiviewPicture multiviewPicture = picture.getAdapter(MultiviewPicture.class); 144 PictureView originalView = multiviewPicture.getView(ORIGINAL_VIEW_TITLE); 145 Blob blob = originalView.getBlob(); 146 if (blob == null) { 147 if (log.isWarnEnabled()) { 148 log.warn(String.format("No Original view Blob found for %s", picture)); 149 } 150 return false; 151 } 152 blob.setFilename(blob.getFilename().replaceAll("^Original_", "")); 153 picture.setPropertyValue(FILE_CONTENT_PROPERTY, (Serializable) blob); 154 picture.setPropertyValue(FILE_FILENAME_PROPERTY, blob.getFilename()); 155 multiviewPicture.removeView(ORIGINAL_VIEW_TITLE); 156 if (picture.isVersion()) { 157 picture.putContextData(ALLOW_VERSION_WRITE, Boolean.TRUE); 158 } 159 // disable quota if installed 160 picture.putContextData(DISABLE_QUOTA_CHECK_LISTENER, Boolean.TRUE); 161 session.saveDocument(picture); 162 return true; 163 } 164 165}