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