001/* 002 * (C) Copyright 2006-2018 Nuxeo (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 */ 017package org.nuxeo.ecm.diff.content.adapter; 018 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.Map; 022import java.util.Set; 023import java.util.function.Consumer; 024import java.util.stream.Collectors; 025 026import org.apache.commons.lang3.StringUtils; 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.nuxeo.ecm.core.api.DocumentModel; 030import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 031import org.nuxeo.ecm.diff.content.ContentDiffAdapter; 032import org.nuxeo.ecm.diff.content.ContentDiffException; 033import org.nuxeo.ecm.diff.content.MimeTypeDescriptor; 034import org.nuxeo.ecm.diff.content.MimeTypesDescriptor; 035import org.nuxeo.ecm.diff.content.adapter.factories.BlobHolderContentDiffAdapterFactory; 036import org.nuxeo.ecm.diff.content.adapter.factories.FileBasedContentDiffAdapterFactory; 037import org.nuxeo.runtime.model.ComponentInstance; 038import org.nuxeo.runtime.model.DefaultComponent; 039 040/** 041 * Runtime component that handles the extension points and the service interface for content diff Adapter management. 042 * 043 * @author Antoine Taillefer 044 */ 045public class ContentDiffAdapterManagerComponent extends DefaultComponent implements ContentDiffAdapterManager { 046 047 public static final String ADAPTER_FACTORY_EP = "adapterFactory"; 048 049 public static final String MIME_TYPE_CONTENT_DIFFER_EP = "mimeTypeContentDiffer"; 050 051 /** 052 * @since 10.10 053 */ 054 public static final String HTML_CONVERSION_BLACKLISTED_MIME_TYPES_EP = "htmlConversionBlacklistedMimeTypes"; 055 056 private static final Log log = LogFactory.getLog(ContentDiffAdapterManagerComponent.class); 057 058 protected Map<String, ContentDiffAdapterFactory> factoryRegistry = new HashMap<>(); 059 060 protected Map<String, MimeTypeContentDiffer> contentDifferFactory = new HashMap<>(); 061 062 protected Map<String, MimeTypeContentDiffer> contentDifferFactoryByName = new HashMap<>(); 063 064 protected Set<String> htmlConversionBlacklistedMimeTypes = new HashSet<>(); 065 066 // Component and EP management 067 068 @Override 069 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 070 071 if (ADAPTER_FACTORY_EP.equals(extensionPoint)) { 072 ContentDiffAdapterFactoryDescriptor desc = (ContentDiffAdapterFactoryDescriptor) contribution; 073 if (desc.isEnabled()) { 074 ContentDiffAdapterFactory factory = desc.getNewInstance(); 075 if (factory != null) { 076 factoryRegistry.put(desc.getTypeName(), factory); 077 } 078 } else { 079 factoryRegistry.remove(desc.getTypeName()); 080 } 081 } else if (MIME_TYPE_CONTENT_DIFFER_EP.equals(extensionPoint)) { 082 MimeTypeContentDifferDescriptor desc = (MimeTypeContentDifferDescriptor) contribution; 083 try { 084 MimeTypeContentDiffer contentDiffer = desc.getKlass().newInstance(); 085 contentDifferFactory.put(desc.getPattern(), contentDiffer); 086 087 // Also (since 7.4) add a name in the contribution 088 String name = desc.getName(); 089 if (StringUtils.isNotBlank(name)) { 090 contentDifferFactoryByName.put(name, contentDiffer); 091 } 092 } catch (ReflectiveOperationException e) { 093 throw new RuntimeException(e); 094 } 095 } else if (HTML_CONVERSION_BLACKLISTED_MIME_TYPES_EP.equals(extensionPoint)) { 096 MimeTypesDescriptor desc = (MimeTypesDescriptor) contribution; 097 if (desc.isOverride()) { 098 // override the whole list 099 htmlConversionBlacklistedMimeTypes = desc.getMimeTypes() 100 .stream() 101 .filter(MimeTypeDescriptor::isEnabled) 102 .map(MimeTypeDescriptor::getName) 103 .collect(Collectors.toSet()); 104 } else { 105 desc.getMimeTypes().forEach(mimeType -> { 106 Consumer<String> consumer = mimeType.isEnabled() ? htmlConversionBlacklistedMimeTypes::add 107 : htmlConversionBlacklistedMimeTypes::remove; 108 consumer.accept(mimeType.getName()); 109 }); 110 } 111 } 112 } 113 114 @Override 115 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 116 } 117 118 // Service interface impl 119 120 @Override 121 public boolean hasAdapter(DocumentModel doc) { 122 if (doc == null) { 123 return false; 124 } 125 126 String docType = doc.getType(); 127 if (factoryRegistry.containsKey(docType)) { 128 return true; 129 } 130 131 return doc.hasSchema("file") || doc.hasSchema("files"); 132 } 133 134 @Override 135 public ContentDiffAdapter getAdapter(DocumentModel doc) { 136 if (doc == null) { 137 return null; 138 } 139 140 String docType = doc.getType(); 141 142 log.debug("Looking for ContentDiffAdapter for type " + docType); 143 144 if (factoryRegistry.containsKey(docType)) { 145 log.debug("Dedicated ContentDiffAdapter factory found"); 146 return factoryRegistry.get(docType).getAdapter(doc); 147 } 148 149 if (doc.isFolder()) { 150 return null; 151 } 152 153 BlobHolder bh = doc.getAdapter(BlobHolder.class); 154 if (bh != null) { 155 log.debug("Using Blob Holder based ContentDiffAdapter factory"); 156 ContentDiffAdapterFactory factory = new BlobHolderContentDiffAdapterFactory(); 157 return factory.getAdapter(doc); 158 159 } 160 161 if (doc.hasSchema("file") || doc.hasSchema("files")) { 162 log.debug("Using default file based ContentDiffAdapter factory"); 163 ContentDiffAdapterFactory factory = new FileBasedContentDiffAdapterFactory(); 164 return factory.getAdapter(doc); 165 } else { 166 return null; 167 } 168 } 169 170 @Override 171 public MimeTypeContentDiffer getContentDiffer(String mimeType) { 172 for (Map.Entry<String, MimeTypeContentDiffer> entry : contentDifferFactory.entrySet()) { 173 if (mimeType.matches(entry.getKey())) { 174 return entry.getValue(); 175 } 176 } 177 return null; 178 } 179 180 @Override 181 public MimeTypeContentDiffer getContentDifferForName(String name) { 182 for (Map.Entry<String, MimeTypeContentDiffer> entry : contentDifferFactoryByName.entrySet()) { 183 if (name.equals(entry.getKey())) { 184 return entry.getValue(); 185 } 186 } 187 return null; 188 } 189 190 @Override 191 public HtmlContentDiffer getHtmlContentDiffer() throws ContentDiffException { 192 MimeTypeContentDiffer htmlContentDiffer = contentDifferFactory.get("text/html"); 193 if (htmlContentDiffer == null || !(htmlContentDiffer instanceof HtmlContentDiffer)) { 194 throw new ContentDiffException( 195 "No content differ of type HtmlContentDiffer found for the 'text/html' mime-type. Please check the 'mimeTypeContentDiffer' contributions."); 196 } 197 return (HtmlContentDiffer) htmlContentDiffer; 198 } 199 200 @Override 201 public Set<String> getHtmlConversionBlacklistedMimeTypes() { 202 return htmlConversionBlacklistedMimeTypes; 203 } 204 205}