KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > repo > content > AbstractContentAccessor


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.repo.content;
18
19 import java.io.IOException JavaDoc;
20 import java.lang.reflect.Method JavaDoc;
21 import java.nio.ByteBuffer JavaDoc;
22 import java.nio.MappedByteBuffer JavaDoc;
23 import java.nio.channels.FileChannel JavaDoc;
24 import java.nio.channels.FileLock JavaDoc;
25 import java.nio.channels.ReadableByteChannel JavaDoc;
26 import java.nio.channels.WritableByteChannel JavaDoc;
27 import java.util.List JavaDoc;
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 /**
41  * Provides basic support for content accessors.
42  *
43  * @author Derek Hulley
44  */

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 JavaDoc[] traceLoggerChannelAssignTrace;
58     
59     /** when set, ensures that listeners are executed within a transaction */
60     private TransactionService transactionService;
61     
62     private String JavaDoc contentUrl;
63     private String JavaDoc mimetype;
64     private String JavaDoc encoding;
65
66     /**
67      * @param contentUrl the content URL
68      */

69     protected AbstractContentAccessor(String JavaDoc contentUrl)
70     {
71         if (contentUrl == null || contentUrl.length() == 0)
72         {
73             throw new IllegalArgumentException JavaDoc("contentUrl must be a valid String");
74         }
75         this.contentUrl = contentUrl;
76         
77         // the default encoding is Java's default encoding
78
encoding = "UTF-8";
79     }
80     
81     @Override JavaDoc
82     protected void finalize() throws Throwable JavaDoc
83     {
84         if (loggerTrace.isDebugEnabled() && traceLoggerChannelAssignTrace != null)
85         {
86             // check that the channel is closed if it was used
87
if (isChannelOpen())
88             {
89                 StringBuilder JavaDoc sb = new StringBuilder JavaDoc(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 JavaDoc toString()
101     {
102         StringBuilder JavaDoc sb = new StringBuilder JavaDoc(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     /**
119      * Provides access to transactions for implementing classes
120      *
121      * @return Returns a source of user transactions
122      */

123     protected TransactionService getTransactionService()
124     {
125         return transactionService;
126     }
127
128     /**
129      * Set the transaction provider to be used by {@link ContentStreamListener listeners}.
130      *
131      * @param transactionService the transaction service to wrap callback code in
132      */

133     public void setTransactionService(TransactionService transactionService)
134     {
135         this.transactionService = transactionService;
136     }
137
138     /**
139      * Derived classes can call this method to ensure that necessary trace logging is performed
140      * when the IO Channel is opened.
141      */

142     protected final void channelOpened()
143     {
144         // trace debug
145
if (loggerTrace.isDebugEnabled())
146         {
147             Exception JavaDoc e = new Exception JavaDoc();
148             e.fillInStackTrace();
149             traceLoggerChannelAssignTrace = e.getStackTrace();
150         }
151     }
152     
153     /**
154      * Derived classes must implement this to help determine if the underlying
155      * IO Channel is still open.
156      *
157      * @return Returns true if the underlying IO Channel is open
158      */

159     protected abstract boolean isChannelOpen();
160     
161     public String JavaDoc getContentUrl()
162     {
163         return contentUrl;
164     }
165     
166     public String JavaDoc getMimetype()
167     {
168         return mimetype;
169     }
170
171     /**
172      * @param mimetype the underlying content's mimetype - null if unknown
173      */

174     public void setMimetype(String JavaDoc mimetype)
175     {
176         this.mimetype = mimetype;
177     }
178
179     /**
180      * @return Returns the content encoding - null if unknown
181      */

182     public String JavaDoc getEncoding()
183     {
184         return encoding;
185     }
186
187     /**
188      * @param encoding the underlying content's encoding - null if unknown
189      */

190     public void setEncoding(String JavaDoc encoding)
191     {
192         this.encoding = encoding;
193     }
194     
195     /**
196      * Generate a callback instance of the {@link FileChannel FileChannel}.
197      *
198      * @param directChannel the delegate that to perform the actual operations
199      * @param listeners the listeners to call
200      * @return Returns a new channel that functions just like the original, except
201      * that it issues callbacks to the listeners
202      * @throws ContentIOException
203      */

204     protected FileChannel JavaDoc getCallbackFileChannel(
205             FileChannel JavaDoc directChannel,
206             List JavaDoc<ContentStreamListener> listeners)
207             throws ContentIOException
208     {
209         FileChannel JavaDoc ret = new CallbackFileChannel(directChannel, listeners);
210         // done
211
return ret;
212     }
213
214     /**
215      * Advise that listens for the completion of specific methods on the
216      * {@link java.nio.channels.ByteChannel} interface.
217      *
218      * @author Derek Hulley
219      */

220     protected class ChannelCloseCallbackAdvise implements AfterReturningAdvice
221     {
222         private List JavaDoc<ContentStreamListener> listeners;
223
224         public ChannelCloseCallbackAdvise(List JavaDoc<ContentStreamListener> listeners)
225         {
226             this.listeners = listeners;
227         }
228         
229         /**
230          * Provides transactional callbacks to the listeners
231          */

232         public void afterReturning(Object JavaDoc returnValue, Method JavaDoc method, Object JavaDoc[] args, Object JavaDoc target) throws Throwable JavaDoc
233         {
234             // check for specific events
235
if (method.getName().equals("close"))
236             {
237                 fireChannelClosed();
238             }
239         }
240         
241         private void fireChannelClosed()
242         {
243             if (listeners.size() == 0)
244             {
245                 // nothing to do
246
return;
247             }
248             TransactionUtil.TransactionWork<Object JavaDoc> work = new TransactionUtil.TransactionWork<Object JavaDoc>()
249                     {
250                         public Object JavaDoc doWork()
251                         {
252                             // call the listeners
253
for (ContentStreamListener listener : listeners)
254                             {
255                                 listener.contentStreamClosed();
256                             }
257                             return null;
258                         }
259                     };
260             if (transactionService != null)
261             {
262                 // just create a transaction
263
TransactionUtil.executeInUserTransaction(transactionService, work);
264             }
265             else
266             {
267                 try
268                 {
269                     work.doWork();
270                 }
271                 catch (Exception JavaDoc e)
272                 {
273                     throw new ContentIOException("Failed to executed channel close callbacks", e);
274                 }
275             }
276             // done
277
if (logger.isDebugEnabled())
278             {
279                 logger.debug("" + listeners.size() + " content listeners called: close");
280             }
281         }
282     }
283     
284     /**
285      * Wraps a <code>FileChannel</code> to provide callbacks to listeners when the
286      * channel is {@link java.nio.channels.Channel#close() closed}.
287      * <p>
288      * This class is unfortunately necessary as the {@link FileChannel} doesn't have
289      * an single interface defining its methods, making it difficult to put an
290      * advice around the methods that require overriding.
291      *
292      * @author Derek Hulley
293      */

294     protected class CallbackFileChannel extends FileChannel JavaDoc
295     {
296         /** the channel to route all calls to */
297         private FileChannel JavaDoc delegate;
298         /** listeners waiting for the stream close */
299         private List JavaDoc<ContentStreamListener> listeners;
300
301         /**
302          * @param delegate the channel that will perform the work
303          * @param listeners listeners for events coming from this channel
304          */

305         public CallbackFileChannel(
306                 FileChannel JavaDoc delegate,
307                 List JavaDoc<ContentStreamListener> listeners)
308         {
309             if (delegate == null)
310             {
311                 throw new IllegalArgumentException JavaDoc("FileChannel delegate is required");
312             }
313             if (delegate instanceof CallbackFileChannel)
314             {
315                 throw new IllegalArgumentException JavaDoc("FileChannel delegate may not be a CallbackFileChannel");
316             }
317             
318             this.delegate = delegate;
319             this.listeners = listeners;
320         }
321         
322         /**
323          * Closes the channel and makes the callbacks to the listeners
324          */

325         @Override JavaDoc
326         protected void implCloseChannel() throws IOException JavaDoc
327         {
328             delegate.close();
329             fireChannelClosed();
330         }
331
332         /**
333          * Helper method to notify stream listeners
334          */

335         private void fireChannelClosed()
336         {
337             if (listeners.size() == 0)
338             {
339                 // nothing to do
340
return;
341             }
342             TransactionUtil.TransactionWork<Object JavaDoc> work = new TransactionUtil.TransactionWork<Object JavaDoc>()
343                     {
344                         public Object JavaDoc doWork()
345                         {
346                             // call the listeners
347
for (ContentStreamListener listener : listeners)
348                             {
349                                 listener.contentStreamClosed();
350                             }
351                             return null;
352                         }
353                     };
354             if (transactionService != null)
355             {
356                 // just create a transaction
357
TransactionUtil.executeInUserTransaction(transactionService, work);
358             }
359             else
360             {
361                 try
362                 {
363                     work.doWork();
364                 }
365                 catch (Exception JavaDoc e)
366                 {
367                     throw new ContentIOException("Failed to executed channel close callbacks", e);
368                 }
369             }
370             // done
371
if (logger.isDebugEnabled())
372             {
373                 logger.debug("" + listeners.size() + " content listeners called: close");
374             }
375         }
376             
377         @Override JavaDoc
378         public void force(boolean metaData) throws IOException JavaDoc
379         {
380             delegate.force(metaData);
381         }
382
383         @Override JavaDoc
384         public FileLock JavaDoc lock(long position, long size, boolean shared) throws IOException JavaDoc
385         {
386             return delegate.lock(position, size, shared);
387         }
388
389         @Override JavaDoc
390         public MappedByteBuffer JavaDoc map(MapMode mode, long position, long size) throws IOException JavaDoc
391         {
392             return delegate.map(mode, position, size);
393         }
394
395         @Override JavaDoc
396         public long position() throws IOException JavaDoc
397         {
398             return delegate.position();
399         }
400
401         @Override JavaDoc
402         public FileChannel JavaDoc position(long newPosition) throws IOException JavaDoc
403         {
404             return delegate.position(newPosition);
405         }
406
407         @Override JavaDoc
408         public int read(ByteBuffer JavaDoc dst) throws IOException JavaDoc
409         {
410             return delegate.read(dst);
411         }
412
413         @Override JavaDoc
414         public int read(ByteBuffer JavaDoc dst, long position) throws IOException JavaDoc
415         {
416             return delegate.read(dst, position);
417         }
418
419         @Override JavaDoc
420         public long read(ByteBuffer JavaDoc[] dsts, int offset, int length) throws IOException JavaDoc
421         {
422             return delegate.read(dsts, offset, length);
423         }
424
425         @Override JavaDoc
426         public long size() throws IOException JavaDoc
427         {
428             return delegate.size();
429         }
430
431         @Override JavaDoc
432         public long transferFrom(ReadableByteChannel JavaDoc src, long position, long count) throws IOException JavaDoc
433         {
434             return delegate.transferFrom(src, position, count);
435         }
436
437         @Override JavaDoc
438         public long transferTo(long position, long count, WritableByteChannel JavaDoc target) throws IOException JavaDoc
439         {
440             return delegate.transferTo(position, count, target);
441         }
442
443         @Override JavaDoc
444         public FileChannel JavaDoc truncate(long size) throws IOException JavaDoc
445         {
446             return delegate.truncate(size);
447         }
448
449         @Override JavaDoc
450         public FileLock JavaDoc tryLock(long position, long size, boolean shared) throws IOException JavaDoc
451         {
452             return delegate.tryLock(position, size, shared);
453         }
454
455         @Override JavaDoc
456         public int write(ByteBuffer JavaDoc src) throws IOException JavaDoc
457         {
458             return delegate.write(src);
459         }
460
461         @Override JavaDoc
462         public int write(ByteBuffer JavaDoc src, long position) throws IOException JavaDoc
463         {
464             return delegate.write(src, position);
465         }
466
467         @Override JavaDoc
468         public long write(ByteBuffer JavaDoc[] srcs, int offset, int length) throws IOException JavaDoc
469         {
470             return delegate.write(srcs, offset, length);
471         }
472     }
473 }
474
Popular Tags