001/* 002 * (C) Copyright 2006-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 * Tiago Cardoso <[email protected]> 018 */ 019package org.nuxeo.ecm.platform.threed.service; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023import org.nuxeo.ecm.core.api.DocumentModel; 024import org.nuxeo.ecm.core.api.IdRef; 025import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 026import org.nuxeo.ecm.core.convert.api.ConverterNotRegistered; 027import org.nuxeo.ecm.core.event.Event; 028import org.nuxeo.ecm.core.event.EventService; 029import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 030import org.nuxeo.ecm.core.work.AbstractWork; 031import org.nuxeo.ecm.core.work.api.WorkManager; 032 033import org.nuxeo.ecm.platform.filemanager.core.listener.MimetypeIconUpdater; 034import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry; 035import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry; 036import org.nuxeo.ecm.platform.threed.BatchConverterHelper; 037import org.nuxeo.ecm.platform.threed.ThreeD; 038import org.nuxeo.ecm.platform.threed.ThreeDDocument; 039import org.nuxeo.ecm.platform.threed.ThreeDInfo; 040import org.nuxeo.ecm.platform.threed.ThreeDRenderView; 041import org.nuxeo.ecm.platform.threed.TransmissionThreeD; 042import org.nuxeo.runtime.api.Framework; 043 044import java.io.Serializable; 045import java.util.ArrayList; 046import java.util.List; 047import java.util.Map; 048import java.util.stream.Collectors; 049 050import static org.nuxeo.ecm.core.api.CoreSession.ALLOW_VERSION_WRITE; 051import static org.nuxeo.ecm.platform.threed.ThreeDConstants.THREED_TYPE; 052import static org.nuxeo.ecm.platform.threed.ThreeDDocumentConstants.MAIN_INFO_PROPERTY; 053import static org.nuxeo.ecm.platform.threed.ThreeDDocumentConstants.RENDER_VIEWS_PROPERTY; 054import static org.nuxeo.ecm.platform.threed.ThreeDDocumentConstants.TRANSMISSIONS_PROPERTY; 055 056/** 057 * Work running batch conversions to update 3D document type preview assets 058 * 059 * @since 8.4 060 */ 061public class ThreeDBatchUpdateWork extends AbstractWork { 062 063 private static final long serialVersionUID = 1L; 064 065 private static final String THREED_CHANGED_EVENT = "threeDEvent"; 066 067 private static final Log log = LogFactory.getLog(ThreeDBatchUpdateWork.class); 068 069 public static final String CATEGORY_THREED_CONVERSION = "threeDConversion"; 070 071 public static final String THREED_CONVERSIONS_DONE_EVENT = "threeDConversionsDone"; 072 073 protected static String computeIdPrefix(String repositoryName, String docId) { 074 return repositoryName + ':' + docId + ":threedbatch:"; 075 } 076 077 public ThreeDBatchUpdateWork(String repositoryName, String docId) { 078 super(computeIdPrefix(repositoryName, docId)); 079 setDocument(repositoryName, docId); 080 } 081 082 @Override 083 public String getCategory() { 084 return CATEGORY_THREED_CONVERSION; 085 } 086 087 @Override 088 public String getTitle() { 089 return "3D preview batch update"; 090 } 091 092 @Override 093 public void work() { 094 if (isWorkInstanceSuspended()) { 095 return; 096 } 097 // Extract 098 setStatus("Extracting"); 099 setProgress(Progress.PROGRESS_INDETERMINATE); 100 101 ThreeD originalThreeD = null; 102 try { 103 openSystemSession(); 104 DocumentModel doc = session.getDocument(new IdRef(docId)); 105 originalThreeD = getThreeDToConvert(doc); 106 commitOrRollbackTransaction(); 107 } finally { 108 cleanUp(true, null); 109 } 110 111 if (originalThreeD == null) { 112 setStatus("Nothing to process"); 113 return; 114 } 115 116 if (isWorkInstanceSuspended()) { 117 return; 118 } 119 // Perform batch conversion 120 setStatus("Batch conversion"); 121 ThreeDService service = Framework.getService(ThreeDService.class); 122 BlobHolder batch; 123 try { 124 batch = service.batchConvert(originalThreeD); 125 } catch (ConverterNotRegistered e) { 126 return; 127 } 128 129 if (isWorkInstanceSuspended()) { 130 return; 131 } 132 // Saving thumbnail to the document 133 setStatus("Saving thumbnail"); 134 List<ThreeDRenderView> threeDRenderViews = BatchConverterHelper.getRenders(batch); 135 long numRenderViews = service.getAutomaticRenderViews().stream().filter(RenderView::isEnabled).count(); 136 if (!threeDRenderViews.isEmpty() && threeDRenderViews.size() == numRenderViews) { 137 try { 138 startTransaction(); 139 openSystemSession(); 140 DocumentModel doc = session.getDocument(new IdRef(docId)); 141 saveNewRenderViews(doc, threeDRenderViews); 142 commitOrRollbackTransaction(); 143 } finally { 144 cleanUp(true, null); 145 } 146 } 147 148 if (isWorkInstanceSuspended()) { 149 return; 150 } 151 // Save 3D blob information 152 setStatus("Saving 3D information on main content"); 153 List<BlobHolder> resources = BatchConverterHelper.getResources(batch); 154 ThreeDInfo mainInfo; 155 mainInfo = BatchConverterHelper.getMainInfo(batch, resources); 156 if (mainInfo != null) { 157 try { 158 startTransaction(); 159 openSystemSession(); 160 DocumentModel doc = session.getDocument(new IdRef(docId)); 161 saveMainInfo(doc, mainInfo); 162 commitOrRollbackTransaction(); 163 } finally { 164 cleanUp(true, null); 165 } 166 } 167 168 if (isWorkInstanceSuspended()) { 169 return; 170 } 171 // Convert transmission formats 172 setStatus("Converting Collada to glTF"); 173 174 List<TransmissionThreeD> colladaThreeDs = BatchConverterHelper.getTransmissions(batch, resources); 175 List<TransmissionThreeD> transmissionThreeDs = colladaThreeDs.stream() 176 .map(service::convertColladaToglTF) 177 .collect(Collectors.toList()); 178 179 if (isWorkInstanceSuspended()) { 180 return; 181 } 182 // Save transmission formats 183 setStatus("Saving transmission formats"); 184 startTransaction(); 185 openSystemSession(); 186 DocumentModel doc = session.getDocument(new IdRef(docId)); 187 saveNewTransmissionThreeDs(doc, transmissionThreeDs); 188 189 if (isWorkInstanceSuspended()) { 190 return; 191 } 192 // Finalize 193 fireThreeDConversionsDoneEvent(doc); 194 setStatus("Done"); 195 } 196 197 protected ThreeD getThreeDToConvert(DocumentModel doc) { 198 ThreeDDocument threedDocument = doc.getAdapter(ThreeDDocument.class); 199 ThreeD threed = threedDocument.getThreeD(); 200 if (threed == null) { 201 log.warn("No original 3d to process for: " + doc); 202 } 203 return threed; 204 } 205 206 protected void saveNewProperties(DocumentModel doc, Serializable properties, String schema) { 207 doc.setPropertyValue(schema, properties); 208 if (doc.isVersion()) { 209 doc.putContextData(ALLOW_VERSION_WRITE, Boolean.TRUE); 210 } 211 session.saveDocument(doc); 212 } 213 214 protected void saveMainInfo(DocumentModel doc, ThreeDInfo info) { 215 saveNewProperties(doc, (Serializable) info.toMap(), MAIN_INFO_PROPERTY); 216 217 } 218 219 protected void saveNewTransmissionThreeDs(DocumentModel doc, List<TransmissionThreeD> transmissionThreeDs) { 220 List<Map<String, Serializable>> transmissionList = new ArrayList<>(); 221 transmissionList.addAll( 222 transmissionThreeDs.stream().map(TransmissionThreeD::toMap).collect(Collectors.toList())); 223 saveNewProperties(doc, (Serializable) transmissionList, TRANSMISSIONS_PROPERTY); 224 } 225 226 protected void saveNewRenderViews(DocumentModel doc, List<ThreeDRenderView> threeDRenderViews) { 227 List<Map<String, Serializable>> renderViewList = new ArrayList<>(); 228 renderViewList.addAll(threeDRenderViews.stream().map(ThreeDRenderView::toMap).collect(Collectors.toList())); 229 230 saveNewProperties(doc, (Serializable) renderViewList, RENDER_VIEWS_PROPERTY); 231 } 232 233 /** 234 * Fire a {@code THREED_CONVERSIONS_DONE_EVENT} event when no other ThreeDBatchUpdateWork is scheduled for this 235 * document. 236 */ 237 protected void fireThreeDConversionsDoneEvent(DocumentModel doc) { 238 WorkManager workManager = Framework.getService(WorkManager.class); 239 List<String> workIds = workManager.listWorkIds(CATEGORY_THREED_CONVERSION, null); 240 String idPrefix = computeIdPrefix(repositoryName, docId); 241 int worksCount = 0; 242 for (String workId : workIds) { 243 if (workId.startsWith(idPrefix)) { 244 if (++worksCount > 1) { 245 // another work scheduled 246 return; 247 } 248 } 249 } 250 251 DocumentEventContext ctx = new DocumentEventContext(session, session.getPrincipal(), doc); 252 Event event = ctx.newEvent(THREED_CONVERSIONS_DONE_EVENT); 253 Framework.getService(EventService.class).fireEvent(event); 254 // force the 3d doc icon 255 MimetypeIconUpdater iconUpdater = new MimetypeIconUpdater(); 256 MimetypeRegistry mimetypeRegistry = Framework.getService(MimetypeRegistry.class); 257 MimetypeEntry mimeTypeEntry = mimetypeRegistry.getMimetypeEntryByMimeType(THREED_TYPE); 258 iconUpdater.updateIconField(mimeTypeEntry, doc); 259 } 260 261}