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 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.core.blob; 020 021import java.io.ByteArrayInputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.net.URI; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.Map; 028import java.util.Set; 029 030import javax.servlet.http.HttpServletRequest; 031 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.nuxeo.ecm.core.api.Blob; 035import org.nuxeo.ecm.core.api.NuxeoException; 036import org.nuxeo.ecm.core.blob.binary.BinaryBlobProvider; 037import org.nuxeo.ecm.core.blob.binary.BinaryManager; 038import org.nuxeo.runtime.model.ComponentContext; 039import org.nuxeo.runtime.model.ComponentInstance; 040import org.nuxeo.runtime.model.DefaultComponent; 041import org.nuxeo.runtime.model.SimpleContributionRegistry; 042 043/** 044 * Implementation of the service managing the storage and retrieval of {@link Blob}s, through internally-registered 045 * {@link BlobProvider}s. 046 * 047 * @since 7.2 048 */ 049public class BlobManagerComponent extends DefaultComponent implements BlobManager { 050 051 private static final Log log = LogFactory.getLog(BlobManagerComponent.class); 052 053 protected static final String XP = "configuration"; 054 055 protected BlobProviderDescriptorRegistry blobProviderDescriptorsRegistry = new BlobProviderDescriptorRegistry(); 056 057 protected Map<String, BlobProvider> blobProviders = new HashMap<>(); 058 059 protected static class BlobProviderDescriptorRegistry extends SimpleContributionRegistry<BlobProviderDescriptor> { 060 061 @Override 062 public String getContributionId(BlobProviderDescriptor contrib) { 063 return contrib.name; 064 } 065 066 @Override 067 public BlobProviderDescriptor clone(BlobProviderDescriptor orig) { 068 return new BlobProviderDescriptor(orig); 069 } 070 071 @Override 072 public void merge(BlobProviderDescriptor src, BlobProviderDescriptor dst) { 073 dst.merge(src); 074 } 075 076 @Override 077 public boolean isSupportingMerge() { 078 return true; 079 } 080 081 public void clear() { 082 currentContribs.clear(); 083 } 084 085 public BlobProviderDescriptor getBlobProviderDescriptor(String id) { 086 return getCurrentContribution(id); 087 } 088 089 public Set<String> getBlobProviderIds() { 090 return currentContribs.keySet(); 091 } 092 } 093 094 @Override 095 public void deactivate(ComponentContext context) { 096 blobProviderDescriptorsRegistry.clear(); 097 // close each blob provider 098 for (BlobProvider blobProvider : blobProviders.values()) { 099 blobProvider.close(); 100 } 101 blobProviders.clear(); 102 } 103 104 @Override 105 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 106 if (XP.equals(extensionPoint)) { 107 if (contribution instanceof BlobProviderDescriptor) { 108 registerBlobProvider((BlobProviderDescriptor) contribution); 109 } else { 110 throw new NuxeoException("Invalid descriptor: " + contribution.getClass()); 111 } 112 } else { 113 throw new NuxeoException("Invalid extension point: " + extensionPoint); 114 } 115 } 116 117 @Override 118 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 119 if (XP.equals(extensionPoint)) { 120 if (contribution instanceof BlobProviderDescriptor) { 121 unregisterBlobProvider((BlobProviderDescriptor) contribution); 122 } 123 } 124 } 125 126 // public for tests 127 public void registerBlobProvider(BlobProviderDescriptor descr) { 128 closeOldBlobProvider(descr.name); 129 blobProviderDescriptorsRegistry.addContribution(descr); 130 // We can't look up the blob provider right now to initialize it, the platform has not started yet 131 // and the blob provider initialization may require a connection that has not been registered yet. 132 } 133 134 // public for tests 135 public void unregisterBlobProvider(BlobProviderDescriptor descr) { 136 closeOldBlobProvider(descr.name); 137 blobProviderDescriptorsRegistry.removeContribution(descr); 138 } 139 140 /** 141 * We're about to change something about a contributed blob provider. Close the old one. 142 */ 143 protected synchronized void closeOldBlobProvider(String id) { 144 BlobProvider blobProvider = blobProviders.remove(id); 145 if (blobProvider != null) { 146 blobProvider.close(); 147 } 148 } 149 150 @Override 151 public synchronized BlobProvider getBlobProvider(String providerId) { 152 BlobProvider blobProvider = blobProviders.get(providerId); 153 if (blobProvider == null) { 154 BlobProviderDescriptor descr = blobProviderDescriptorsRegistry.getBlobProviderDescriptor(providerId); 155 if (descr == null) { 156 return null; 157 } 158 Class<?> klass = descr.klass; 159 Map<String, String> properties = descr.properties; 160 try { 161 if (BlobProvider.class.isAssignableFrom(klass)) { 162 @SuppressWarnings("unchecked") 163 Class<? extends BlobProvider> blobProviderClass = (Class<? extends BlobProvider>) klass; 164 blobProvider = blobProviderClass.newInstance(); 165 } else if (BinaryManager.class.isAssignableFrom(klass)) { 166 @SuppressWarnings("unchecked") 167 Class<? extends BinaryManager> binaryManagerClass = (Class<? extends BinaryManager>) klass; 168 BinaryManager binaryManager = binaryManagerClass.newInstance(); 169 blobProvider = new BinaryBlobProvider(binaryManager); 170 } else { 171 throw new RuntimeException("Unknown class for blob provider: " + klass); 172 } 173 } catch (ReflectiveOperationException e) { 174 throw new RuntimeException(e); 175 } 176 try { 177 blobProvider.initialize(providerId, properties); 178 } catch (IOException e) { 179 throw new RuntimeException(e); 180 } 181 blobProviders.put(providerId, blobProvider); 182 } 183 return blobProvider; 184 } 185 186 @Override 187 public BlobProvider getBlobProvider(Blob blob) { 188 if (!(blob instanceof ManagedBlob)) { 189 return null; 190 } 191 ManagedBlob managedBlob = (ManagedBlob) blob; 192 return getBlobProvider(managedBlob.getProviderId()); 193 } 194 195 @Override 196 public InputStream getStream(Blob blob) throws IOException { 197 BlobProvider blobProvider = getBlobProvider(blob); 198 if (blobProvider == null) { 199 return null; 200 } 201 try { 202 return blobProvider.getStream((ManagedBlob) blob); 203 } catch (IOException e) { 204 // we don't want to crash everything if the remote file cannot be accessed 205 log.error("Failed to access file: " + ((ManagedBlob) blob).getKey(), e); 206 return new ByteArrayInputStream(new byte[0]); 207 } 208 } 209 210 @Override 211 public InputStream getThumbnail(Blob blob) throws IOException { 212 BlobProvider blobProvider = getBlobProvider(blob); 213 if (blobProvider == null) { 214 return null; 215 } 216 return blobProvider.getThumbnail((ManagedBlob) blob); 217 } 218 219 @Override 220 public URI getURI(Blob blob, UsageHint hint, HttpServletRequest servletRequest) throws IOException { 221 BlobProvider blobProvider = getBlobProvider(blob); 222 if (blobProvider == null) { 223 return null; 224 } 225 return blobProvider.getURI((ManagedBlob) blob, hint, servletRequest); 226 } 227 228 @Override 229 public Map<String, URI> getAvailableConversions(Blob blob, UsageHint hint) throws IOException { 230 BlobProvider blobProvider = getBlobProvider(blob); 231 if (blobProvider == null) { 232 return Collections.emptyMap(); 233 } 234 return blobProvider.getAvailableConversions((ManagedBlob) blob, hint); 235 } 236 237 @Override 238 public synchronized Map<String, BlobProvider> getBlobProviders() { 239 Set<String> blobProviderIds = blobProviderDescriptorsRegistry.getBlobProviderIds(); 240 if (blobProviders.size() != blobProviderIds.size()) { 241 // register all providers 242 for (String id : blobProviderIds) { 243 getBlobProvider(id); // instantiate and initialize 244 } 245 } 246 return blobProviders; 247 } 248 249}