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
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
56
57 /** Creates a new instance of SimpleFileSystemRepository. */
58 public SimpleFileSystemRepository(){
59 }
60
61
62
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
71 if (root != null && root.startsWith("${join.home}")){
72 BootstrapManager manager = BootstrapUtil.getBootstrapManager();
73 root = manager.getJoinHome() + root.substring(12);
74 }
75
76 if (root != null){
77 rootFile = new File(root);
78 if (!rootFile.isDirectory()){
79
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
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
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
113 File parentDir = getParentDirectory(artifact);
114
115 File[] files = parentDir.listFiles(getArtifactFilenameFilter(artifact));
116
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
135 return new FileInputStream(artifactFile);
136 }
137 catch (Exception e){
138
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
154 File parentDir = getParentDirectory(artifact);
155
156 File targetFile = null;
157 if (content.getName().lastIndexOf(".") != -1){
158
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
165 targetFile = new File(parentDir, artifact.getUniqueId());
166 log.debug("Content has no extension. TargetFile path is " + targetFile.getPath());
167 }
168
169
170 FileInputStream contentFis = null;
171 try{
172 targetFile.createNewFile();
173 contentFis = new FileInputStream(content);
174 }
175 catch (Exception e){
176
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
193 File parentDir = getParentDirectory(artifact);
194
195 File targetFile = new File(parentDir, artifact.getUniqueId());
196 log.debug("Content is an InputStream. TargetFile path is " + targetFile.getPath());
197
198
199 writeFile(is, targetFile);
200 }
201
202
203
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
213 String structurePath = getStructurePath(artifact);
214
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
233 fos = new FileOutputStream(targetFile);
234
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
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) {
248 try {fos.close();}
249 catch (Exception e) {
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 }