1 17 package org.alfresco.repo.content; 18 19 import java.io.BufferedOutputStream ; 20 import java.io.ByteArrayInputStream ; 21 import java.io.File ; 22 import java.io.FileInputStream ; 23 import java.io.IOException ; 24 import java.io.InputStream ; 25 import java.io.OutputStream ; 26 import java.nio.channels.Channels ; 27 import java.nio.channels.FileChannel ; 28 import java.nio.channels.ReadableByteChannel ; 29 import java.nio.channels.WritableByteChannel ; 30 import java.util.ArrayList ; 31 import java.util.List ; 32 33 import org.alfresco.error.AlfrescoRuntimeException; 34 import org.alfresco.repo.content.filestore.FileContentWriter; 35 import org.alfresco.service.cmr.repository.ContentAccessor; 36 import org.alfresco.service.cmr.repository.ContentIOException; 37 import org.alfresco.service.cmr.repository.ContentReader; 38 import org.alfresco.service.cmr.repository.ContentStreamListener; 39 import org.alfresco.service.cmr.repository.ContentWriter; 40 import org.alfresco.util.TempFileProvider; 41 import org.apache.commons.logging.Log; 42 import org.apache.commons.logging.LogFactory; 43 import org.springframework.aop.framework.ProxyFactory; 44 import org.springframework.util.FileCopyUtils; 45 46 55 public abstract class AbstractContentWriter extends AbstractContentAccessor implements ContentWriter 56 { 57 private static final Log logger = LogFactory.getLog(AbstractContentWriter.class); 58 59 private List <ContentStreamListener> listeners; 60 private WritableByteChannel channel; 61 private ContentReader existingContentReader; 62 63 67 protected AbstractContentWriter(String contentUrl, ContentReader existingContentReader) 68 { 69 super(contentUrl); 70 this.existingContentReader = existingContentReader; 71 72 listeners = new ArrayList <ContentStreamListener>(2); 73 } 74 75 78 protected ContentReader getExistingContentReader() 79 { 80 return existingContentReader; 81 } 82 83 87 public synchronized void addListener(ContentStreamListener listener) 88 { 89 if (channel != null) 90 { 91 throw new RuntimeException ("Channel is already in use"); 92 } 93 listeners.add(listener); 94 } 95 96 108 protected abstract ContentReader createReader() throws ContentIOException; 109 110 113 public final ContentReader getReader() throws ContentIOException 114 { 115 if (!isClosed()) 116 { 117 return null; 118 } 119 ContentReader reader = createReader(); 120 if (reader == null) 121 { 122 throw new AlfrescoRuntimeException("ContentReader failed to create new reader: \n" + 123 " writer: " + this); 124 } 125 else if (reader.getContentUrl() == null || !reader.getContentUrl().equals(getContentUrl())) 126 { 127 throw new AlfrescoRuntimeException("ContentReader has different URL: \n" + 128 " writer: " + this + "\n" + 129 " new reader: " + reader); 130 } 131 reader.setMimetype(this.getMimetype()); 133 reader.setEncoding(this.getEncoding()); 134 if (logger.isDebugEnabled()) 136 { 137 logger.debug("Writer spawned new reader: \n" + 138 " writer: " + this + "\n" + 139 " new reader: " + reader); 140 } 141 return reader; 142 } 143 144 147 public synchronized final boolean isClosed() 148 { 149 if (channel != null) 150 { 151 return !channel.isOpen(); 152 } 153 else 154 { 155 return false; 156 } 157 } 158 159 160 protected boolean isChannelOpen() 161 { 162 if (channel != null) 163 { 164 return channel.isOpen(); 165 } 166 else 167 { 168 return false; 169 } 170 } 171 172 182 protected abstract WritableByteChannel getDirectWritableChannel() throws ContentIOException; 183 184 192 private WritableByteChannel getCallbackWritableChannel( 193 WritableByteChannel directChannel, 194 List <ContentStreamListener> listeners) 195 throws ContentIOException 196 { 197 WritableByteChannel callbackChannel = null; 198 if (directChannel instanceof FileChannel ) 199 { 200 callbackChannel = getCallbackFileChannel((FileChannel ) directChannel, listeners); 201 } 202 else 203 { 204 ChannelCloseCallbackAdvise advise = new ChannelCloseCallbackAdvise(listeners); 206 ProxyFactory proxyFactory = new ProxyFactory(directChannel); 207 proxyFactory.addAdvice(advise); 208 callbackChannel = (WritableByteChannel ) proxyFactory.getProxy(); 209 } 210 if (logger.isDebugEnabled()) 212 { 213 logger.debug("Created callback byte channel: \n" + 214 " original: " + directChannel + "\n" + 215 " new: " + callbackChannel); 216 } 217 return callbackChannel; 218 } 219 220 224 public synchronized final WritableByteChannel getWritableChannel() throws ContentIOException 225 { 226 if (channel != null) 228 { 229 throw new RuntimeException ("A channel has already been opened"); 230 } 231 WritableByteChannel directChannel = getDirectWritableChannel(); 232 channel = getCallbackWritableChannel(directChannel, listeners); 233 234 super.channelOpened(); 236 if (logger.isDebugEnabled()) 238 { 239 logger.debug("Opened channel onto content: \n" + 240 " content: " + this + "\n" + 241 " channel: " + channel); 242 } 243 return channel; 244 } 245 246 249 public FileChannel getFileChannel(boolean truncate) throws ContentIOException 250 { 251 261 262 channel = getWritableChannel(); 264 FileChannel clientFileChannel = null; 266 if (channel instanceof FileChannel ) 267 { 268 clientFileChannel = (FileChannel ) channel; 270 if (!truncate && existingContentReader != null) 272 { 273 ReadableByteChannel existingContentChannel = existingContentReader.getReadableChannel(); 274 long existingContentLength = existingContentReader.getSize(); 275 try 277 { 278 clientFileChannel.transferFrom(existingContentChannel, 0, existingContentLength); 279 if (logger.isDebugEnabled()) 281 { 282 logger.debug("Copied content for random access: \n" + 283 " writer: " + this + "\n" + 284 " existing: " + existingContentReader); 285 } 286 } 287 catch (IOException e) 288 { 289 throw new ContentIOException("Failed to copy from existing content to enable random access: \n" + 290 " writer: " + this + "\n" + 291 " existing: " + existingContentReader, 292 e); 293 } 294 finally 295 { 296 try { existingContentChannel.close(); } catch (IOException e) {} 297 } 298 } 299 if (logger.isDebugEnabled()) 301 { 302 logger.debug("Content writer provided direct support for FileChannel: \n" + 303 " writer: " + this); 304 } 305 } 306 else 307 { 308 File tempFile = TempFileProvider.createTempFile("random_write_spoof_", ".bin"); 311 final FileContentWriter spoofWriter = new FileContentWriter( 312 tempFile, getExistingContentReader()); ContentStreamListener spoofListener = new ContentStreamListener() 318 { 319 public void contentStreamClosed() throws ContentIOException 320 { 321 ContentReader spoofReader = spoofWriter.getReader(); 323 FileChannel spoofChannel = spoofReader.getFileChannel(); 324 try 326 { 327 long spoofFileSize = spoofChannel.size(); 328 spoofChannel.transferTo(0, spoofFileSize, channel); 329 } 330 catch (IOException e) 331 { 332 throw new ContentIOException("Failed to copy from spoofed temporary channel to permanent channel: \n" + 333 " writer: " + this + "\n" + 334 " temp: " + spoofReader, 335 e); 336 } 337 finally 338 { 339 try { spoofChannel.close(); } catch (Throwable e) {} 340 try 341 { 342 channel.close(); 343 } 344 catch (IOException e) 345 { 346 throw new ContentIOException("Failed to close underlying channel", e); 347 } 348 } 349 } 350 }; 351 spoofWriter.addListener(spoofListener); 352 clientFileChannel = spoofWriter.getFileChannel(truncate); 354 if (logger.isDebugEnabled()) 356 { 357 logger.debug("Content writer provided indirect support for FileChannel: \n" + 358 " writer: " + this + "\n" + 359 " temp writer: " + spoofWriter); 360 } 361 } 362 return clientFileChannel; 364 } 365 366 369 public OutputStream getContentOutputStream() throws ContentIOException 370 { 371 try 372 { 373 WritableByteChannel channel = getWritableChannel(); 374 OutputStream is = new BufferedOutputStream (Channels.newOutputStream(channel)); 375 return is; 377 } 378 catch (Throwable e) 379 { 380 throw new ContentIOException("Failed to open stream onto channel: \n" + 381 " writer: " + this, 382 e); 383 } 384 } 385 386 390 public void putContent(ContentReader reader) throws ContentIOException 391 { 392 try 393 { 394 InputStream is = reader.getContentInputStream(); 396 putContent(is); 398 } 399 catch (Throwable e) 400 { 401 throw new ContentIOException("Failed to copy reader content to writer: \n" + 402 " writer: " + this + "\n" + 403 " source reader: " + reader, 404 e); 405 } 406 } 407 408 public final void putContent(InputStream is) throws ContentIOException 409 { 410 try 411 { 412 OutputStream os = getContentOutputStream(); 413 FileCopyUtils.copy(is, os); } 416 catch (IOException e) 417 { 418 throw new ContentIOException("Failed to copy content from input stream: \n" + 419 " writer: " + this, 420 e); 421 } 422 } 423 424 public final void putContent(File file) throws ContentIOException 425 { 426 try 427 { 428 OutputStream os = getContentOutputStream(); 429 FileInputStream is = new FileInputStream (file); 430 FileCopyUtils.copy(is, os); } 433 catch (IOException e) 434 { 435 throw new ContentIOException("Failed to copy content from file: \n" + 436 " writer: " + this + "\n" + 437 " file: " + file, 438 e); 439 } 440 } 441 442 447 public final void putContent(String content) throws ContentIOException 448 { 449 try 450 { 451 String encoding = getEncoding(); 453 byte[] bytes = (encoding == null) ? content.getBytes() : content.getBytes(encoding); 454 OutputStream os = getContentOutputStream(); 456 ByteArrayInputStream is = new ByteArrayInputStream (bytes); 457 FileCopyUtils.copy(is, os); } 460 catch (IOException e) 461 { 462 throw new ContentIOException("Failed to copy content from string: \n" + 463 " writer: " + this + 464 " content length: " + content.length(), 465 e); 466 } 467 } 468 } 469 | Popular Tags |