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.core.InvalidParameterException;
18  import org.figure8.join.core.setup.BootstrapUtil;
19  import org.figure8.join.core.setup.BootstrapManager;
20  import org.figure8.join.businessobjects.artifact.Artifact;
21  import org.figure8.join.util.LogUtil;
22  
23  import org.apache.commons.logging.Log;
24  
25  import java.io.File;
26  import java.io.InputStream;
27  import java.io.FileInputStream;
28  import java.io.FileOutputStream;
29  import java.io.FilenameFilter;
30  /**
31   * This is a simple implementation of a {@link Repository} using the local
32   * file system as a storage area for {@link Artifact}s.<br/>
33   * This implementation defines the following properties :<ul>
34   * <li>rootDirectory: Path to the root of file system repository,</li>
35   * <li>structureDefinition: Definition of repository strusture using Artifact
36   * related properties that are ${release}, ${uniqueId}, ${versionInfo} and
37   * ${categoryInfo}. The default structure is: ${categoryInfo}/${release},</li>
38   * </ul>
39   * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
40   * @version $Revision: 1.3 $
41   */
42  public class SimpleFileSystemRepository extends AbstractStructuredRepository{
43  
44     // Attributes ---------------------------------------------------------------
45  
46     /** Get a commons logger. */
47     private static final Log log = LogUtil.getLog(SimpleFileSystemRepository.class);
48  
49     /** File object representing rootDirectory. */
50     private File rootFile = null;
51     /** Path to the root of file system repository. */
52     private String rootDirectory = null;
53  
54  
55     // Constructors -------------------------------------------------------------
56  
57     /** Creates a new instance of SimpleFileSystemRepository. */
58     public SimpleFileSystemRepository(){
59     }
60  
61  
62     // Public -------------------------------------------------------------------
63  
64     /**
65      * Sets the path to root of the file system repository.
66      * @param root Path to the file system repository
67      * @throws InvalidParameterException if root is null or not a valid directory
68      */
69     public void setRootDirectory(String root) throws InvalidParameterException{
70        // Replace ${join.home} in root.
71        if (root != null && root.startsWith("${join.home}")){
72           BootstrapManager manager = BootstrapUtil.getBootstrapManager();
73           root = manager.getJoinHome() + root.substring(12);
74        }
75        // Check directory existence.
76        if (root != null){
77           rootFile = new File(root);
78           if (!rootFile.isDirectory()){
79              // Check if parent exists.
80              if (!rootFile.getParentFile().isDirectory()){
81                 log.error("The rootDirectory property (" + root + ") parent is not a valid directory");
82                 throw new InvalidParameterException("The rootDirectory property parent is not a valid directory");
83              }
84              // Create this one.
85              rootFile.mkdir();
86           }
87        }
88        else{
89           log.error("The rootDirectory property cannot be null !");
90           throw new InvalidParameterException("The rootDirectory property cannot be null !");
91        }
92        this.rootDirectory = root;
93     }
94  
95     /** @return Path to the file system repository */
96     public String getRootDirectory(){
97        return rootDirectory;
98     }
99  
100 
101    // Implementation of Repository ---------------------------------------------
102 
103    /**
104     * Retrieve the input stream corresponding to a given Artifact. This
105     * method can be used for artifact content downloading or retrieval.
106     * @param artifact The domain object representing artifact to retrieve
107     * @return An {@code InputStream} on artifact content
108     * @throws ConnectionException if physical repository cannot be connected
109     * @throws TransferException if something wrong occur after connection, during transfer
110     */
111    public InputStream getArtifact(Artifact artifact) throws ConnectionException, TransferException{
112       // Get parent directory for artifact.
113       File parentDir = getParentDirectory(artifact);
114       // We don't know file name (it may have an extension or not), so list them ...
115       File[] files = parentDir.listFiles(getArtifactFilenameFilter(artifact));
116       // Repository should contain only one valid file.
117       if (files != null && files.length > 1){
118          log.error("There's more than one file corresponding to artifact " + artifact.getUniqueId());
119          throw new ConnectionException("Repository is in an unsafe state",
120                  new IllegalStateException("More than one file for artifact"));
121       }
122       if (files == null || files.length == 0){
123          log.error("There's no file coresponding to artifact " + artifact.getUniqueId());
124          throw new ConnectionException("Repository is in an unsafe state",
125                  new IllegalStateException("No file available for artifact"));
126       }
127       File artifactFile = files[0];
128       if (!artifactFile.isFile()){
129          log.error("ArtifactFile is not a valid file: " + artifactFile.getPath());
130          throw new ConnectionException("Repository is in an unsafe state",
131                  new IllegalStateException("No valid fail for artifact"));
132       }
133       try{
134          // Return file input stream.
135          return new FileInputStream(artifactFile);
136       }
137       catch (Exception e){
138          // Log and wrap this into a TransferException.
139          log.error("Exception while creating input stream onto file " + artifactFile.getPath());
140          throw new TransferException("Exception while creating input stream on artifact", e);
141       }
142    }
143 
144    /**
145     * Store the content of a given Artifact within repository datastore.
146     * @param artifact The domain object representing artifact to store
147     * @param content File representing artifact content (may be a directory)
148     * @throws ConnectionException if physical repository cannot be connected
149     * @throws TransferException if something wrong occur after connection, during transfer
150     */
151    public void storeArtifact(Artifact artifact, File content) throws ConnectionException, TransferException{
152       log.info("Storing file of artifact with id: " + artifact.getUniqueId());
153       // Get parent directory for artifact.
154       File parentDir = getParentDirectory(artifact);
155       // Build the target file by checking extension.
156       File targetFile = null;
157       if (content.getName().lastIndexOf(".") != -1){
158          // If there's extension, concat to artifact id.
159          targetFile = new File(parentDir, artifact.getUniqueId() +
160                  content.getName().substring(content.getName().lastIndexOf(".")));
161          log.debug("Content has an extension. TargetFile path is " + targetFile.getPath());
162       }
163       else{
164          // If no extension, file name = identifier.
165          targetFile = new File(parentDir, artifact.getUniqueId());
166          log.debug("Content has no extension. TargetFile path is " + targetFile.getPath());
167       }
168 
169       // Create an input stream and write file.
170       FileInputStream contentFis = null;
171       try{
172          targetFile.createNewFile();
173          contentFis = new FileInputStream(content);
174       }
175       catch (Exception e){
176          // Log and wrap exception into a connection exception.
177          log.error("Exception while creating the file into repository: " + targetFile.getPath());
178          throw new ConnectionException("Exception while creating target file into repository", e);
179       }
180       writeFile(contentFis, targetFile);
181    }
182 
183    /**
184     * Store the content of a given Artifact within repository datastore using an input stream.
185     * @param artifact The domain object representing artifact to store
186     * @param is InputStream on artifact content
187     * @throws ConnectionException if physical repository cannot be connected
188     * @throws TransferException if something wrong occur after connection, during transfer
189     */
190    public void storeArtifact(Artifact artifact, InputStream is) throws ConnectionException, TransferException{
191       log.info("Storing input stream  of artifact with id: " + artifact.getUniqueId());
192       // Get parent directory for artifact.
193       File parentDir = getParentDirectory(artifact);
194       // Build the target file.
195       File targetFile = new File(parentDir, artifact.getUniqueId());
196       log.debug("Content is an InputStream. TargetFile path is " + targetFile.getPath());
197 
198       // Write target file.
199       writeFile(is, targetFile);
200    }
201 
202 
203    // Private ------------------------------------------------------------------
204 
205    /**
206     * Retrieve the parent directory in the repository for the given
207     * Artifact. If this directory does not exists yet, it creates it.
208     * @param artifact The artifact to get parent directory for.
209     * @return The existing parent directory for storing artifact
210     */
211    private File getParentDirectory(Artifact artifact){
212       // Get structure path for artifact.
213       String structurePath = getStructurePath(artifact);
214       // Ensure the parent directory exists.
215       File parentDir = new File(rootFile, structurePath);
216       if (!parentDir.isDirectory())
217          parentDir.mkdirs();
218 
219       return parentDir;
220    }
221 
222    /**
223     * Write an input stream content to a target file. This method closes
224     * the input stream at the end of write operation.
225     * @param is The input stream to use for getting data
226     * @param targetFile The target file to write data to
227     * @throws TransferException if an IOException occurs during writing file
228     */
229    private void writeFile(InputStream is, File targetFile) throws TransferException{
230       FileOutputStream fos = null;
231       try{
232          // Opening the output stream.
233          fos = new FileOutputStream(targetFile);
234          // Read is and write on fos unsing a buffer of bytes.
235          int bytes = 0;
236          byte[] buffer = new byte[8192];
237          while ((bytes = is.read(buffer, 0, 8192)) != -1)
238             fos.write(buffer, 0, bytes);
239       }
240       catch (Exception e){
241          // Log and wrap IOException into a transfer exception.
242          log.error("IOException while writing into file: " + targetFile.getPath());
243          throw new TransferException("IOException while writing into target file", e);
244       }
245       finally{
246          try {is.close();}
247          catch (Exception e) {/* Do nothing here... */}
248          try {fos.close();}
249          catch (Exception e) {/* Do nothing here... */}
250       }
251    }
252 
253    /**
254     * Get a FilenameFilter implementation for filtering files
255     * corresponding to artifact.
256     * @return A FilenameFilter for artifact
257     */
258    private FilenameFilter getArtifactFilenameFilter(final Artifact artifact){
259       return new FilenameFilter(){
260          public boolean accept(File dir, String name){
261             return name.startsWith(artifact.getUniqueId());
262          }
263       };
264    }
265 }