View Javadoc

1   /**
2    * Copyright 2005-2006 the original author or authors.
3    *
4    * Licensed under the Gnu General Pubic License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with
6    * the License. You may obtain a copy of the License at
7    *
8    *      http://www.opensource.org/licenses/gpl-license.php
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13   * See the Gnu General Public License for more details.
14   */
15  package org.figure8.join.services.repository;
16  
17  import org.figure8.join.businessobjects.artifact.Artifact;
18  import org.figure8.join.util.LogUtil;
19  
20  import org.apache.commons.vfs.VFS;
21  import org.apache.commons.vfs.FileType;
22  import org.apache.commons.vfs.FileObject;
23  import org.apache.commons.vfs.FileSelector;
24  import org.apache.commons.vfs.FileSelectInfo;
25  import org.apache.commons.vfs.FileSystemManager;
26  import org.apache.commons.vfs.FileSystemException;
27  import org.apache.commons.logging.Log;
28  
29  import java.io.File;
30  import java.io.InputStream;
31  import java.io.OutputStream;
32  import java.io.FileInputStream;
33  /**
34   * This is an implementation of {@link Repository} using Jakarta VFS Commons.
35   * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
36   * @version $Revision: 1.2 $
37   */
38  public class VFSRepository extends AbstractStructuredRepository{
39  
40     // Static -------------------------------------------------------------------
41     
42     /** Get a commons logger. */
43     private static final Log log = LogUtil.getLog(VFSRepository.class);
44     
45     
46     // Attributes ---------------------------------------------------------------
47  
48     /** String representation of the repository base url */
49     private String baseUrl = null;
50     
51     
52     // Constructors -------------------------------------------------------------
53  
54     /** Creates a new instance of VFSRepository. */
55     public VFSRepository(){
56     }
57     
58  
59     // Public -------------------------------------------------------------------
60     
61     /**
62      * Get the base url (string representation) of the physical repository to access
63      * @return The base url of the repository
64      */
65     public String getBaseUrl(){
66        return baseUrl;
67     }
68     /**
69      * Set the base url (string representation) of the physical repository to access
70      * @param baseUrl The base url of the repository
71      */   
72     public void setBaseUrl(String baseUrl){
73        if (!baseUrl.endsWith("/"))
74           baseUrl += "/";
75        this.baseUrl = baseUrl;
76     }
77     
78     
79     // Implementation of Repository ---------------------------------------------
80  
81     /**
82      * Retrieve the input stream corresponding to a given Artifact. This
83      * method can be used for artifact content downloading or retrieval.
84      * @param artifact The domain object representing artifact to retrieve
85      * @return An {@code InputStream} on artifact content
86      * @throws ConnectionException if physical repository cannot be connected
87      * @throws TransferException if something wrong occur after connection, during transfer
88      */
89     public InputStream getArtifact(Artifact artifact) throws ConnectionException, TransferException{
90        log.info("Retrieving stream of artifact with id: " + artifact.getUniqueId());
91        // Get structure path for artifact.
92        String structurePath = getStructurePath(artifact);
93        if (!structurePath.endsWith("/"))
94           structurePath += "/";
95        
96        // Build a path for target file.
97        String target = baseUrl + structurePath;
98        // We don't know file name (it may have an extension or not), so list them ...
99        FileObject[] files = null;
100       try{
101          FileSystemManager fsManager = VFS.getManager();
102          FileObject parentDir = fsManager.resolveFile(structurePath);
103          parentDir.findFiles(getArtifactFileSelector(artifact));
104       }
105       catch (FileSystemException fse){
106          // Log and wrap exception into a connection exception.
107          log.error("Exception while resolving the file into repository: " + target);
108          throw new ConnectionException("Exception while resolving target file into repository", fse);
109       }
110       // Repository should contain only one valid file.
111       if (files == null || files.length == 0){
112          log.error("There's no file coresponding to artifact " + artifact.getUniqueId());
113          throw new ConnectionException("Repository is in an unsafe state",
114                  new IllegalStateException("No file available for artifact"));
115       }
116       FileObject artifactFile = files[0];
117       try{
118          // Check it is a file (not a directory).
119          if (!artifactFile.getType().equals(FileType.FILE)){
120             log.error("ArtifactFile is not a valid file: " + artifactFile.getName().toString());
121             throw new ConnectionException("Repository is in an unsafe state",
122                     new IllegalStateException("No valid file for artifact"));
123          }
124          // Check it is readable.
125          if (!artifactFile.isReadable()){
126             log.error("ArtifactFile is not a readable file: " + artifactFile.getName().toString());
127             throw new ConnectionException("Repository is in an unsafe state",
128                     new IllegalStateException("File for artifact not readable"));
129          }
130       }
131       catch (FileSystemException fse){
132          // Log and wrap exception into a connection exception.
133          log.error("Exception while accessing the properties of file: " + artifactFile.getName().toString());
134          throw new ConnectionException("Exception while accessing the properties of artifact file", fse);
135       }
136       try{
137          // Return input stream on artifact file content.
138          return artifactFile.getContent().getInputStream();
139       }
140       catch (Exception e){
141          // Log and wrap this into a TransferException.
142          log.error("Exception while creating input stream onto file " + artifactFile.getName().toString());
143          throw new TransferException("Exception while creating input stream on artifact", e);
144       }
145    }
146 
147    /**
148     * Store the content of a given Artifact within repository datastore.
149     * @param artifact The domain object representing artifact to store
150     * @param content File representing artifact content (may be a directory)
151     * @throws ConnectionException if physical repository cannot be connected
152     * @throws TransferException if something wrong occur after connection, during transfer
153     */
154    public void storeArtifact(Artifact artifact, File content) throws ConnectionException, TransferException{
155       log.info("Storing file of artifact with id: " + artifact.getUniqueId());
156       // Get structure path for artifact.
157       String structurePath = getStructurePath(artifact);
158       if (!structurePath.endsWith("/"))
159          structurePath += "/";
160       
161       // Build a path for target file.
162       String target = baseUrl + structurePath;
163       if (content.getName().lastIndexOf(".") != -1){
164          // If there's extension, concat to artifact id.
165          target += artifact.getUniqueId() + content.getName().substring(content.getName().lastIndexOf("."));
166          log.debug("Content has an extension. Target path is " + target);
167       }
168       else{
169          // If no extension, file name = identifier.
170          target += artifact.getUniqueId();
171          log.debug("Content has no extension. Target path is " + target);
172       }
173 
174       // Resolve destination file.
175       FileObject dest = null;
176       try{
177          FileSystemManager fsManager = VFS.getManager();
178          dest = fsManager.resolveFile(target);
179       }
180       catch (FileSystemException fse){
181          // Log and wrap exception into a connection exception.
182          log.error("Exception while resolving the file into repository: " + target);
183          throw new ConnectionException("Exception while resolving target file into repository", fse);
184       }
185 
186       // Create an input and output streams for writing file.
187       OutputStream os = null;
188       FileInputStream contentFis = null;
189       try{
190          os = dest.getContent().getOutputStream();
191          contentFis = new FileInputStream(content);
192       }
193       catch (Exception e){
194          e.printStackTrace();
195          // Log and wrap exception into a connection exception.
196          log.error("Exception while opening copy streams into repository for: " + target);
197          throw new ConnectionException("Exception while opening copy streams into repository", e);
198       }
199       writeStream(contentFis, os);
200    }
201    
202    /**
203     * Store the content of a given Artifact within repository datastore using an input stream.
204     * @param artifact The domain object representing artifact to store
205     * @param is InputStream on artifact content
206     * @throws ConnectionException if physical repository cannot be connected
207     * @throws TransferException if something wrong occur after connection, during transfer
208     */
209    public void storeArtifact(Artifact artifact, InputStream is) throws ConnectionException, TransferException{
210       log.info("Storing stream of artifact with id: " + artifact.getUniqueId());
211       // Get structure path for artifact.
212       String structurePath = getStructurePath(artifact);
213       if (!structurePath.endsWith("/"))
214          structurePath += "/";
215 
216       // Build a path for target file.
217       String target = baseUrl + structurePath + artifact.getUniqueId();
218       log.debug("Content is an InputStream. Target file path is " + target);
219 
220       // Resolve destination file.
221       FileObject dest = null;
222       try{
223          FileSystemManager fsManager = VFS.getManager();
224          dest = fsManager.resolveFile(target);
225       }
226       catch (FileSystemException fse){
227          // Log and wrap exception into a connection exception.
228          log.error("Exception while resolving the file into repository: " + target);
229          throw new ConnectionException("Exception while resolving target file into repository", fse);
230       }
231 
232       // Create an input and output streams for writing file.
233       OutputStream os = null;
234       try {os = dest.getContent().getOutputStream();}
235       catch (Exception e){
236          // Log and wrap exception into a connection exception.
237          log.error("Exception while opening copy streams into repository for: " + target);
238          throw new ConnectionException("Exception while opening copy streams into repository", e);
239       }
240       writeStream(is, os);
241    }
242 
243 
244    // Private ------------------------------------------------------------------
245 
246    /**
247     * Write an input stream content to a target output stream. This method closes
248     * the input and output streams at the end of write operation.
249     * @param is The input stream to use for getting data
250     * @param os The output stream to use for writing data
251     * @throws TransferException if an IOException occurs during writing output
252     */
253    private void writeStream(InputStream is, OutputStream os) throws TransferException{
254       try{
255          // Read is and write on fos unsing a buffer of bytes.
256          int bytes = 0;
257          byte[] buffer = new byte[8192];
258          while ((bytes = is.read(buffer, 0, 8192)) != -1)
259             os.write(buffer, 0, bytes);
260       }
261       catch (Exception e){
262          // Log and wrap IOException into a transfer exception.
263          log.error("IOException while writing into stream: " + os);
264          throw new TransferException("IOException while writing into target stream", e);
265       }
266       finally{
267          try {is.close();}
268          catch (Exception e) {/* Do nothing here... */}
269          try {os.close();}
270          catch (Exception e) {/* Do nothing here... */}
271       }
272    }
273 
274    /**
275     * Get a FileSelector implementation for filtering files
276     * corresponding to artifact.
277     * @return A FileSelector for artifact
278     */
279    private FileSelector getArtifactFileSelector(final Artifact artifact){
280       return new FileSelector(){
281          public boolean includeFile(FileSelectInfo info){
282             return info.getFile().getName().getBaseName().startsWith(artifact.getUniqueId());
283          }
284          public boolean traverseDescendents(FileSelectInfo info){
285             return false;
286          }
287       };
288    }
289 }