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