1 17 package org.alfresco.repo.content; 18 19 import java.io.BufferedInputStream ; 20 import java.io.ByteArrayOutputStream ; 21 import java.io.File ; 22 import java.io.FileOutputStream ; 23 import java.io.IOException ; 24 import java.io.InputStream ; 25 import java.io.InputStreamReader ; 26 import java.io.OutputStream ; 27 import java.io.Reader ; 28 import java.nio.channels.Channels ; 29 import java.nio.channels.FileChannel ; 30 import java.nio.channels.ReadableByteChannel ; 31 import java.util.ArrayList ; 32 import java.util.List ; 33 34 import org.alfresco.error.AlfrescoRuntimeException; 35 import org.alfresco.repo.content.filestore.FileContentWriter; 36 import org.alfresco.service.cmr.repository.ContentAccessor; 37 import org.alfresco.service.cmr.repository.ContentIOException; 38 import org.alfresco.service.cmr.repository.ContentReader; 39 import org.alfresco.service.cmr.repository.ContentStreamListener; 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 AbstractContentReader extends AbstractContentAccessor implements ContentReader 56 { 57 private static final Log logger = LogFactory.getLog(AbstractContentReader.class); 58 59 private List <ContentStreamListener> listeners; 60 private ReadableByteChannel channel; 61 62 66 protected AbstractContentReader(String contentUrl) 67 { 68 super(contentUrl); 69 70 listeners = new ArrayList <ContentStreamListener>(2); 71 } 72 73 77 public synchronized void addListener(ContentStreamListener listener) 78 { 79 if (channel != null) 80 { 81 throw new RuntimeException ("Channel is already in use"); 82 } 83 listeners.add(listener); 84 } 85 86 97 protected abstract ContentReader createReader() throws ContentIOException; 98 99 102 public final ContentReader getReader() throws ContentIOException 103 { 104 ContentReader reader = createReader(); 105 if (reader == null) 106 { 107 throw new AlfrescoRuntimeException("ContentReader failed to create new reader: \n" + 108 " reader: " + this); 109 } 110 else if (reader.getContentUrl() == null || !reader.getContentUrl().equals(getContentUrl())) 111 { 112 throw new AlfrescoRuntimeException("ContentReader has different URL: \n" + 113 " reader: " + this + "\n" + 114 " new reader: " + reader); 115 } 116 reader.setMimetype(this.getMimetype()); 118 reader.setEncoding(this.getEncoding()); 119 if (logger.isDebugEnabled()) 121 { 122 logger.debug("Reader spawned new reader: \n" + 123 " reader: " + this + "\n" + 124 " new reader: " + reader); 125 } 126 return reader; 127 } 128 129 132 public synchronized final boolean isClosed() 133 { 134 if (channel != null) 135 { 136 return !channel.isOpen(); 137 } 138 else 139 { 140 return false; 141 } 142 } 143 144 145 protected boolean isChannelOpen() 146 { 147 if (channel != null) 148 { 149 return channel.isOpen(); 150 } 151 else 152 { 153 return false; 154 } 155 } 156 157 168 protected abstract ReadableByteChannel getDirectReadableChannel() throws ContentIOException; 169 170 178 private ReadableByteChannel getCallbackReadableChannel( 179 ReadableByteChannel directChannel, 180 List <ContentStreamListener> listeners) 181 throws ContentIOException 182 { 183 ReadableByteChannel callbackChannel = null; 184 if (directChannel instanceof FileChannel ) 185 { 186 callbackChannel = getCallbackFileChannel((FileChannel ) directChannel, listeners); 187 } 188 else 189 { 190 ChannelCloseCallbackAdvise advise = new ChannelCloseCallbackAdvise(listeners); 192 ProxyFactory proxyFactory = new ProxyFactory(directChannel); 193 proxyFactory.addAdvice(advise); 194 callbackChannel = (ReadableByteChannel ) proxyFactory.getProxy(); 195 } 196 if (logger.isDebugEnabled()) 198 { 199 logger.debug("Created callback byte channel: \n" + 200 " original: " + directChannel + "\n" + 201 " new: " + callbackChannel); 202 } 203 return callbackChannel; 204 } 205 206 210 public synchronized final ReadableByteChannel getReadableChannel() throws ContentIOException 211 { 212 if (channel != null) 214 { 215 throw new RuntimeException ("A channel has already been opened"); 216 } 217 ReadableByteChannel directChannel = getDirectReadableChannel(); 218 channel = getCallbackReadableChannel(directChannel, listeners); 219 220 super.channelOpened(); 222 if (logger.isDebugEnabled()) 224 { 225 logger.debug("Opened channel onto content: " + this); 226 } 227 return channel; 228 } 229 230 231 234 public FileChannel getFileChannel() throws ContentIOException 235 { 236 241 242 channel = getReadableChannel(); 244 FileChannel clientFileChannel = null; 246 if (channel instanceof FileChannel ) 247 { 248 clientFileChannel = (FileChannel ) channel; 250 if (logger.isDebugEnabled()) 252 { 253 logger.debug("Content reader provided direct support for FileChannel: \n" + 254 " reader: " + this); 255 } 256 } 257 else 258 { 259 File tempFile = TempFileProvider.createTempFile("random_read_spoof_", ".bin"); 262 FileContentWriter spoofWriter = new FileContentWriter(tempFile); 263 FileChannel spoofWriterChannel = spoofWriter.getFileChannel(false); 265 try 266 { 267 long spoofFileSize = this.getSize(); 268 spoofWriterChannel.transferFrom(channel, 0, spoofFileSize); 269 } 270 catch (IOException e) 271 { 272 throw new ContentIOException("Failed to copy from permanent channel to spoofed temporary channel: \n" + 273 " reader: " + this + "\n" + 274 " temp: " + spoofWriter, 275 e); 276 } 277 finally 278 { 279 try { spoofWriterChannel.close(); } catch (IOException e) {} 280 } 281 final ContentReader spoofReader = spoofWriter.getReader(); 283 ContentStreamListener spoofListener = new ContentStreamListener() 286 { 287 public void contentStreamClosed() throws ContentIOException 288 { 289 try 290 { 291 channel.close(); 292 } 293 catch (IOException e) 294 { 295 throw new ContentIOException("Failed to close underlying channel", e); 296 } 297 } 298 }; 299 spoofReader.addListener(spoofListener); 300 clientFileChannel = spoofReader.getFileChannel(); 302 if (logger.isDebugEnabled()) 304 { 305 logger.debug("Content writer provided indirect support for FileChannel: \n" + 306 " writer: " + this + "\n" + 307 " temp writer: " + spoofWriter); 308 } 309 } 310 return clientFileChannel; 312 } 313 314 317 public InputStream getContentInputStream() throws ContentIOException 318 { 319 try 320 { 321 ReadableByteChannel channel = getReadableChannel(); 322 InputStream is = new BufferedInputStream (Channels.newInputStream(channel)); 323 return is; 325 } 326 catch (Throwable e) 327 { 328 throw new ContentIOException("Failed to open stream onto channel: \n" + 329 " accessor: " + this, 330 e); 331 } 332 } 333 334 338 public final void getContent(OutputStream os) throws ContentIOException 339 { 340 try 341 { 342 InputStream is = getContentInputStream(); 343 FileCopyUtils.copy(is, os); } 346 catch (IOException e) 347 { 348 throw new ContentIOException("Failed to copy content to output stream: \n" + 349 " accessor: " + this, 350 e); 351 } 352 } 353 354 public final void getContent(File file) throws ContentIOException 355 { 356 try 357 { 358 InputStream is = getContentInputStream(); 359 FileOutputStream os = new FileOutputStream (file); 360 FileCopyUtils.copy(is, os); } 363 catch (IOException e) 364 { 365 throw new ContentIOException("Failed to copy content to file: \n" + 366 " accessor: " + this + "\n" + 367 " file: " + file, 368 e); 369 } 370 } 371 372 public final String getContentString(int length) throws ContentIOException 373 { 374 if (length < 0 || length > Integer.MAX_VALUE) 375 { 376 throw new IllegalArgumentException ("Character count must be positive and within range"); 377 } 378 Reader reader = null; 379 try 380 { 381 char[] buffer = new char[length]; 383 384 String encoding = getEncoding(); 385 if (encoding == null) 387 { 388 reader = new InputStreamReader (getContentInputStream()); 389 } 390 else 391 { 392 reader = new InputStreamReader (getContentInputStream(), encoding); 393 } 394 int count = reader.read(buffer, 0, length); 396 String result = new String (buffer, 0, count); 398 return result; 400 } 401 catch (IOException e) 402 { 403 throw new ContentIOException("Failed to copy content to string: \n" + 404 " accessor: " + this + "\n" + 405 " length: " + length, 406 e); 407 } 408 finally 409 { 410 if (reader != null) 411 { 412 try { reader.close(); } catch (Throwable e) { logger.error(e); } 413 } 414 } 415 } 416 417 425 public final String getContentString() throws ContentIOException 426 { 427 try 428 { 429 InputStream is = getContentInputStream(); 431 ByteArrayOutputStream os = new ByteArrayOutputStream (); 432 FileCopyUtils.copy(is, os); byte[] bytes = os.toByteArray(); 434 String encoding = getEncoding(); 436 String content = (encoding == null) ? new String (bytes) : new String (bytes, encoding); 438 return content; 440 } 441 catch (IOException e) 442 { 443 throw new ContentIOException("Failed to copy content to string: \n" + 444 " accessor: " + this, 445 e); 446 } 447 } 448 } 449 | Popular Tags |