KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > filesys > smb > server > repo > ContentNetworkFile


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.filesys.smb.server.repo;
18
19 import java.io.FileNotFoundException JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.nio.ByteBuffer JavaDoc;
22 import java.nio.channels.FileChannel JavaDoc;
23
24 import org.alfresco.error.AlfrescoRuntimeException;
25 import org.alfresco.filesys.server.filesys.AccessDeniedException;
26 import org.alfresco.filesys.server.filesys.FileAttribute;
27 import org.alfresco.filesys.server.filesys.FileInfo;
28 import org.alfresco.filesys.server.filesys.FileOpenParams;
29 import org.alfresco.filesys.server.filesys.NetworkFile;
30 import org.alfresco.i18n.I18NUtil;
31 import org.alfresco.model.ContentModel;
32 import org.alfresco.repo.content.filestore.FileContentReader;
33 import org.alfresco.repo.transaction.TransactionUtil;
34 import org.alfresco.repo.transaction.TransactionUtil.TransactionWork;
35 import org.alfresco.service.cmr.repository.ContentAccessor;
36 import org.alfresco.service.cmr.repository.ContentData;
37 import org.alfresco.service.cmr.repository.ContentReader;
38 import org.alfresco.service.cmr.repository.ContentService;
39 import org.alfresco.service.cmr.repository.ContentWriter;
40 import org.alfresco.service.cmr.repository.NodeRef;
41 import org.alfresco.service.cmr.repository.NodeService;
42 import org.alfresco.service.transaction.TransactionService;
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45
46 /**
47  * Implementation of the <tt>NetworkFile</tt> for direct interaction
48  * with the channel repository.
49  * <p>
50  * This provides the interaction with the Alfresco Content Model file/folder structure.
51  *
52  * @author Derek Hulley
53  */

54 public class ContentNetworkFile extends NetworkFile
55 {
56     private static final Log logger = LogFactory.getLog(ContentNetworkFile.class);
57     
58     private TransactionService transactionService;
59     private NodeService nodeService;
60     private ContentService contentService;
61     private NodeRef nodeRef;
62     /** keeps track of the read/write access */
63     private FileChannel JavaDoc channel;
64     /** the original content opened */
65     private ContentAccessor content;
66     /** keeps track of any writes */
67     private boolean modified;
68     
69     // Flag to indicate if the file channel is writable
70

71     private boolean writableChannel;
72
73     /**
74      * Helper method to create a {@link NetworkFile network file} given a node reference.
75      */

76     public static ContentNetworkFile createFile(
77             TransactionService transactionService,
78             NodeService nodeService,
79             ContentService contentService,
80             CifsHelper cifsHelper,
81             NodeRef nodeRef,
82             FileOpenParams params)
83     {
84         String JavaDoc path = params.getPath();
85         
86         // Check write access
87
// TODO: Check access writes and compare to write requirements
88

89         // create the file
90
ContentNetworkFile netFile = new ContentNetworkFile(transactionService, nodeService, contentService, nodeRef, path);
91         // set relevant parameters
92
if (params.isReadOnlyAccess())
93         {
94             netFile.setGrantedAccess(NetworkFile.READONLY);
95         }
96         else
97         {
98             netFile.setGrantedAccess(NetworkFile.READWRITE);
99         }
100         
101         // check the type
102
FileInfo fileInfo;
103         try
104         {
105             fileInfo = cifsHelper.getFileInformation(nodeRef, "");
106         }
107         catch (FileNotFoundException JavaDoc e)
108         {
109             throw new AlfrescoRuntimeException("File not found when creating network file: " + nodeRef, e);
110         }
111         if (fileInfo.isDirectory())
112         {
113             netFile.setAttributes(FileAttribute.Directory);
114         }
115         else
116         {
117             // Set the current size
118

119             netFile.setFileSize(fileInfo.getSize());
120         }
121         
122         // Set the file timestamps
123

124         if ( fileInfo.hasCreationDateTime())
125             netFile.setCreationDate( fileInfo.getCreationDateTime());
126         
127         if ( fileInfo.hasModifyDateTime())
128             netFile.setModifyDate(fileInfo.getModifyDateTime());
129         
130         if ( fileInfo.hasAccessDateTime())
131             netFile.setAccessDate(fileInfo.getAccessDateTime());
132         
133         // Set the file attributes
134

135         netFile.setAttributes(fileInfo.getFileAttributes());
136
137         // If the file is read-only then only allow read access
138

139         if ( netFile.isReadOnly())
140             netFile.setGrantedAccess(NetworkFile.READONLY);
141         
142         // done
143
if (logger.isDebugEnabled())
144         {
145             logger.debug("Created network file: \n" +
146                     " node: " + nodeRef + "\n" +
147                     " param: " + params + "\n" +
148                     " netfile: " + netFile);
149         }
150         return netFile;
151     }
152
153     /**
154      * Class constructor
155      *
156      * @param transactionService TransactionService
157      * @param nodeService NodeService
158      * @param contentService ContentService
159      * @param nodeRef NodeRef
160      * @param name String
161      */

162     private ContentNetworkFile(
163             TransactionService transactionService,
164             NodeService nodeService,
165             ContentService contentService,
166             NodeRef nodeRef,
167             String JavaDoc name)
168     {
169         super(name);
170         setFullName(name);
171         this.transactionService = transactionService;
172         this.nodeService = nodeService;
173         this.contentService = contentService;
174         this.nodeRef = nodeRef;
175     }
176
177     /**
178      * Return the file details as a string
179      *
180      * @return String
181      */

182     public String JavaDoc toString()
183     {
184         StringBuilder JavaDoc sb = new StringBuilder JavaDoc(50);
185         sb.append("ContentNetworkFile:")
186           .append("[ node=").append(nodeRef)
187           .append(", channel=").append(channel)
188           .append(writableChannel ? "(Write)" : "(Read)")
189           .append(", writable=").append(isWritable())
190           .append(", content=").append(content)
191           .append(", modified=").append(modified)
192           .append("]");
193         return sb.toString();
194     }
195     
196     /**
197      * @return Returns the node reference representing this file
198      */

199     public NodeRef getNodeRef()
200     {
201         return nodeRef;
202     }
203     
204     /**
205      * @return Returns true if the channel should be writable
206      *
207      * @see NetworkFile#getGrantedAccess()
208      * @see NetworkFile#READONLY
209      * @see NetworkFile#WRITEONLY
210      * @see NetworkFile#READWRITE
211      */

212     private boolean isWritable()
213     {
214         // check that we are allowed to write
215
int access = getGrantedAccess();
216         return (access == NetworkFile.READWRITE || access == NetworkFile.WRITEONLY);
217     }
218
219     /**
220      * Determine if the file content data has been opened
221      *
222      * @return boolean
223      */

224     public final boolean hasContent()
225     {
226         return content != null ? true : false;
227     }
228     
229     /**
230      * Opens the channel for reading or writing depending on the access mode.
231      * <p>
232      * If the channel is already open, it is left.
233      *
234      * @param write true if the channel must be writable
235      * @param trunc true if the writable channel does not require the previous content data
236      * @throws AccessDeniedException if this network file is read only
237      * @throws AlfrescoRuntimeException if this network file represents a directory
238      *
239      * @see NetworkFile#getGrantedAccess()
240      * @see NetworkFile#READONLY
241      * @see NetworkFile#WRITEONLY
242      * @see NetworkFile#READWRITE
243      */

244     private synchronized void openContent(boolean write, boolean trunc) throws AccessDeniedException, AlfrescoRuntimeException
245     {
246         if (isDirectory())
247         {
248             throw new AlfrescoRuntimeException("Unable to open channel for a directory network file: " + this);
249         }
250         
251         // Check if write access is required and the current channel is read-only
252

253         else if ( write && writableChannel == false && channel != null)
254         {
255             // Close the existing read-only channel
256

257             try
258             {
259                 channel.close();
260                 channel = null;
261             }
262             catch (IOException JavaDoc ex)
263             {
264                 logger.error("Error closing read-only channel", ex);
265             }
266             
267             // Debug
268

269             if ( logger.isDebugEnabled())
270                 logger.debug("Switching to writable channel for " + getName());
271         }
272         else if (channel != null)
273         {
274             // already have channel open
275
return;
276         }
277         
278         // we need to create the channel
279
if (write && !isWritable())
280         {
281             throw new AccessDeniedException("The network file was created for read-only: " + this);
282         }
283
284         content = null;
285         if (write)
286         {
287             content = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, false);
288             
289             // Indicate that we have a writable channel to the file
290

291             writableChannel = true;
292             
293             // Get the writable channel, do not copy existing content data if the file is to be truncated
294

295             channel = ((ContentWriter) content).getFileChannel( trunc);
296         }
297         else
298         {
299             content = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
300             // ensure that the content we are going to read is valid
301
content = FileContentReader.getSafeContentReader(
302                     (ContentReader) content,
303                     I18NUtil.getMessage(FileContentReader.MSG_MISSING_CONTENT),
304                     nodeRef, content);
305             
306             // Indicate that we only have a read-only channel to the data
307

308             writableChannel = false;
309             
310             // get the read-only channel
311
channel = ((ContentReader) content).getFileChannel();
312         }
313     }
314
315     /**
316      * Close the file
317      *
318      * @exception IOException
319      */

320     public synchronized void closeFile() throws IOException JavaDoc
321     {
322         if (isDirectory()) // ignore if this is a directory
323
{
324             return;
325         }
326         else if (channel == null) // ignore if the channel hasn't been opened
327
{
328             return;
329         }
330         
331         
332         if (modified) // file was modified
333
{
334             // execute the close (with possible replication listeners, etc) and the
335
// node update in the same transaction. A transaction will be started
336
// by the nodeService anyway, so it is merely widening the transaction
337
// boundaries.
338
TransactionWork<Object JavaDoc> closeWork = new TransactionWork<Object JavaDoc>()
339             {
340                 public Object JavaDoc doWork() throws Exception JavaDoc
341                 {
342                     // close the channel
343
// close it
344
channel.close();
345                     channel = null;
346                     // update node properties
347
ContentData contentData = content.getContentData();
348                     nodeService.setProperty(nodeRef, ContentModel.PROP_CONTENT, contentData);
349                     // done
350
return null;
351                 }
352             };
353             TransactionUtil.executeInUserTransaction(transactionService, closeWork);
354         }
355         else
356         {
357             // close it - it was not modified
358
channel.close();
359             channel = null;
360             // no transaction used here. Any listener operations against this (now unused) content
361
// are irrelevant.
362
}
363     }
364
365     /**
366      * Truncate or extend the file to the specified length
367      *
368      * @param size long
369      * @exception IOException
370      */

371     public synchronized void truncateFile(long size) throws IOException JavaDoc
372     {
373         // If the content data channel has not been opened yet and the requested size is zero
374
// then this is an open for overwrite so the existing content data is not copied
375

376         if ( hasContent() == false && size == 0L)
377         {
378             // Open content for overwrite, no need to copy existing content data
379

380             openContent(true, true);
381         }
382         else
383         {
384             // Normal open for write
385

386             openContent(true, false);
387
388             // Truncate or extend the channel
389

390             channel.truncate(size);
391         }
392         
393         // Set modification flag
394

395         modified = true;
396         
397         // Debug
398

399         if (logger.isDebugEnabled())
400         {
401             logger.debug("Truncated channel: " +
402                     " net file: " + this + "\n" +
403                     " size: " + size);
404         }
405     }
406
407     /**
408      * Write a block of data to the file.
409      *
410      * @param buf byte[]
411      * @param len int
412      * @param pos int
413      * @param fileOff long
414      * @exception IOException
415      */

416     public synchronized void writeFile(byte[] buffer, int length, int position, long fileOffset) throws IOException JavaDoc
417     {
418         // Open the channel for writing
419

420         openContent(true, false);
421         
422         // Write to the channel
423

424         ByteBuffer JavaDoc byteBuffer = ByteBuffer.wrap(buffer, position, length);
425         int count = channel.write(byteBuffer, fileOffset);
426         
427         // Set modification flag
428

429         modified = true;
430
431         // Update the current file size
432

433         setFileSize(channel.size());
434         
435         // DEBUG
436

437         if (logger.isDebugEnabled())
438         {
439             logger.debug("Wrote to channel: " +
440                     " net file: " + this + "\n" +
441                     " written: " + count);
442         }
443     }
444
445     /**
446      * Read from the file.
447      *
448      * @param buf byte[]
449      * @param len int
450      * @param pos int
451      * @param fileOff long
452      * @return Length of data read.
453      * @exception IOException
454      */

455     public synchronized int readFile(byte[] buffer, int length, int position, long fileOffset) throws IOException JavaDoc
456     {
457         // Open the channel for reading
458

459         openContent(false, false);
460         
461         // read from the channel
462
ByteBuffer JavaDoc byteBuffer = ByteBuffer.wrap(buffer, position, length);
463         int count = channel.read(byteBuffer, fileOffset);
464         if (count < 0)
465         {
466             count = 0; // doesn't obey the same rules, i.e. just returns the bytes read
467
}
468         // done
469
if (logger.isDebugEnabled())
470         {
471             logger.debug("Read from channel: " +
472                     " net file: " + this + "\n" +
473                     " read: " + count);
474         }
475         return count;
476     }
477     
478     @Override JavaDoc
479     public synchronized void openFile(boolean createFlag) throws IOException JavaDoc
480     {
481         throw new UnsupportedOperationException JavaDoc();
482     }
483
484     @Override JavaDoc
485     public synchronized long seekFile(long pos, int typ) throws IOException JavaDoc
486     {
487         throw new UnsupportedOperationException JavaDoc();
488     }
489
490     @Override JavaDoc
491     public synchronized void flushFile() throws IOException JavaDoc
492     {
493         // open the channel for writing
494
openContent(true, false);
495         // flush the channel - metadata flushing is not important
496
channel.force(false);
497         // done
498
if (logger.isDebugEnabled())
499         {
500             logger.debug("Flushed channel: " +
501                     " net file: " + this);
502         }
503     }
504 }
505
Popular Tags