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