1 17 package org.alfresco.repo.content; 18 19 import java.io.IOException ; 20 import java.lang.reflect.Method ; 21 import java.nio.ByteBuffer ; 22 import java.nio.MappedByteBuffer ; 23 import java.nio.channels.FileChannel ; 24 import java.nio.channels.FileLock ; 25 import java.nio.channels.ReadableByteChannel ; 26 import java.nio.channels.WritableByteChannel ; 27 import java.util.List ; 28 29 import org.alfresco.error.StackTraceUtil; 30 import org.alfresco.repo.transaction.TransactionUtil; 31 import org.alfresco.service.cmr.repository.ContentAccessor; 32 import org.alfresco.service.cmr.repository.ContentData; 33 import org.alfresco.service.cmr.repository.ContentIOException; 34 import org.alfresco.service.cmr.repository.ContentStreamListener; 35 import org.alfresco.service.transaction.TransactionService; 36 import org.apache.commons.logging.Log; 37 import org.apache.commons.logging.LogFactory; 38 import org.springframework.aop.AfterReturningAdvice; 39 40 45 public abstract class AbstractContentAccessor implements ContentAccessor 46 { 47 private static Log logger = LogFactory.getLog(AbstractContentAccessor.class); 48 private static final Log loggerTrace = LogFactory.getLog(AbstractContentAccessor.class.getName() + ".trace"); 49 static 50 { 51 if (loggerTrace.isDebugEnabled()) 52 { 53 loggerTrace.warn("Trace channel assignment logging is on and will affect performance"); 54 } 55 } 56 57 private StackTraceElement [] traceLoggerChannelAssignTrace; 58 59 60 private TransactionService transactionService; 61 62 private String contentUrl; 63 private String mimetype; 64 private String encoding; 65 66 69 protected AbstractContentAccessor(String contentUrl) 70 { 71 if (contentUrl == null || contentUrl.length() == 0) 72 { 73 throw new IllegalArgumentException ("contentUrl must be a valid String"); 74 } 75 this.contentUrl = contentUrl; 76 77 encoding = "UTF-8"; 79 } 80 81 @Override 82 protected void finalize() throws Throwable 83 { 84 if (loggerTrace.isDebugEnabled() && traceLoggerChannelAssignTrace != null) 85 { 86 if (isChannelOpen()) 88 { 89 StringBuilder sb = new StringBuilder (1024); 90 StackTraceUtil.buildStackTrace( 91 "Content IO Channel was opened but not closed: \n" + this, 92 traceLoggerChannelAssignTrace, 93 sb, 94 -1); 95 loggerTrace.error(sb); 96 } 97 } 98 } 99 100 public String toString() 101 { 102 StringBuilder sb = new StringBuilder (100); 103 sb.append("ContentAccessor") 104 .append("[ contentUrl=").append(getContentUrl()) 105 .append(", mimetype=").append(getMimetype()) 106 .append(", size=").append(getSize()) 107 .append(", encoding=").append(getEncoding()) 108 .append("]"); 109 return sb.toString(); 110 } 111 112 public ContentData getContentData() 113 { 114 ContentData property = new ContentData(contentUrl, mimetype, getSize(), encoding); 115 return property; 116 } 117 118 123 protected TransactionService getTransactionService() 124 { 125 return transactionService; 126 } 127 128 133 public void setTransactionService(TransactionService transactionService) 134 { 135 this.transactionService = transactionService; 136 } 137 138 142 protected final void channelOpened() 143 { 144 if (loggerTrace.isDebugEnabled()) 146 { 147 Exception e = new Exception (); 148 e.fillInStackTrace(); 149 traceLoggerChannelAssignTrace = e.getStackTrace(); 150 } 151 } 152 153 159 protected abstract boolean isChannelOpen(); 160 161 public String getContentUrl() 162 { 163 return contentUrl; 164 } 165 166 public String getMimetype() 167 { 168 return mimetype; 169 } 170 171 174 public void setMimetype(String mimetype) 175 { 176 this.mimetype = mimetype; 177 } 178 179 182 public String getEncoding() 183 { 184 return encoding; 185 } 186 187 190 public void setEncoding(String encoding) 191 { 192 this.encoding = encoding; 193 } 194 195 204 protected FileChannel getCallbackFileChannel( 205 FileChannel directChannel, 206 List <ContentStreamListener> listeners) 207 throws ContentIOException 208 { 209 FileChannel ret = new CallbackFileChannel(directChannel, listeners); 210 return ret; 212 } 213 214 220 protected class ChannelCloseCallbackAdvise implements AfterReturningAdvice 221 { 222 private List <ContentStreamListener> listeners; 223 224 public ChannelCloseCallbackAdvise(List <ContentStreamListener> listeners) 225 { 226 this.listeners = listeners; 227 } 228 229 232 public void afterReturning(Object returnValue, Method method, Object [] args, Object target) throws Throwable 233 { 234 if (method.getName().equals("close")) 236 { 237 fireChannelClosed(); 238 } 239 } 240 241 private void fireChannelClosed() 242 { 243 if (listeners.size() == 0) 244 { 245 return; 247 } 248 TransactionUtil.TransactionWork<Object > work = new TransactionUtil.TransactionWork<Object >() 249 { 250 public Object doWork() 251 { 252 for (ContentStreamListener listener : listeners) 254 { 255 listener.contentStreamClosed(); 256 } 257 return null; 258 } 259 }; 260 if (transactionService != null) 261 { 262 TransactionUtil.executeInUserTransaction(transactionService, work); 264 } 265 else 266 { 267 try 268 { 269 work.doWork(); 270 } 271 catch (Exception e) 272 { 273 throw new ContentIOException("Failed to executed channel close callbacks", e); 274 } 275 } 276 if (logger.isDebugEnabled()) 278 { 279 logger.debug("" + listeners.size() + " content listeners called: close"); 280 } 281 } 282 } 283 284 294 protected class CallbackFileChannel extends FileChannel 295 { 296 297 private FileChannel delegate; 298 299 private List <ContentStreamListener> listeners; 300 301 305 public CallbackFileChannel( 306 FileChannel delegate, 307 List <ContentStreamListener> listeners) 308 { 309 if (delegate == null) 310 { 311 throw new IllegalArgumentException ("FileChannel delegate is required"); 312 } 313 if (delegate instanceof CallbackFileChannel) 314 { 315 throw new IllegalArgumentException ("FileChannel delegate may not be a CallbackFileChannel"); 316 } 317 318 this.delegate = delegate; 319 this.listeners = listeners; 320 } 321 322 325 @Override 326 protected void implCloseChannel() throws IOException 327 { 328 delegate.close(); 329 fireChannelClosed(); 330 } 331 332 335 private void fireChannelClosed() 336 { 337 if (listeners.size() == 0) 338 { 339 return; 341 } 342 TransactionUtil.TransactionWork<Object > work = new TransactionUtil.TransactionWork<Object >() 343 { 344 public Object doWork() 345 { 346 for (ContentStreamListener listener : listeners) 348 { 349 listener.contentStreamClosed(); 350 } 351 return null; 352 } 353 }; 354 if (transactionService != null) 355 { 356 TransactionUtil.executeInUserTransaction(transactionService, work); 358 } 359 else 360 { 361 try 362 { 363 work.doWork(); 364 } 365 catch (Exception e) 366 { 367 throw new ContentIOException("Failed to executed channel close callbacks", e); 368 } 369 } 370 if (logger.isDebugEnabled()) 372 { 373 logger.debug("" + listeners.size() + " content listeners called: close"); 374 } 375 } 376 377 @Override 378 public void force(boolean metaData) throws IOException 379 { 380 delegate.force(metaData); 381 } 382 383 @Override 384 public FileLock lock(long position, long size, boolean shared) throws IOException 385 { 386 return delegate.lock(position, size, shared); 387 } 388 389 @Override 390 public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException 391 { 392 return delegate.map(mode, position, size); 393 } 394 395 @Override 396 public long position() throws IOException 397 { 398 return delegate.position(); 399 } 400 401 @Override 402 public FileChannel position(long newPosition) throws IOException 403 { 404 return delegate.position(newPosition); 405 } 406 407 @Override 408 public int read(ByteBuffer dst) throws IOException 409 { 410 return delegate.read(dst); 411 } 412 413 @Override 414 public int read(ByteBuffer dst, long position) throws IOException 415 { 416 return delegate.read(dst, position); 417 } 418 419 @Override 420 public long read(ByteBuffer [] dsts, int offset, int length) throws IOException 421 { 422 return delegate.read(dsts, offset, length); 423 } 424 425 @Override 426 public long size() throws IOException 427 { 428 return delegate.size(); 429 } 430 431 @Override 432 public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException 433 { 434 return delegate.transferFrom(src, position, count); 435 } 436 437 @Override 438 public long transferTo(long position, long count, WritableByteChannel target) throws IOException 439 { 440 return delegate.transferTo(position, count, target); 441 } 442 443 @Override 444 public FileChannel truncate(long size) throws IOException 445 { 446 return delegate.truncate(size); 447 } 448 449 @Override 450 public FileLock tryLock(long position, long size, boolean shared) throws IOException 451 { 452 return delegate.tryLock(position, size, shared); 453 } 454 455 @Override 456 public int write(ByteBuffer src) throws IOException 457 { 458 return delegate.write(src); 459 } 460 461 @Override 462 public int write(ByteBuffer src, long position) throws IOException 463 { 464 return delegate.write(src, position); 465 } 466 467 @Override 468 public long write(ByteBuffer [] srcs, int offset, int length) throws IOException 469 { 470 return delegate.write(srcs, offset, length); 471 } 472 } 473 } 474 | Popular Tags |