View Javadoc
1   /*
2    * Copyright 2009-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.springframework.batch.admin.service;
17  
18  import org.apache.commons.io.FileUtils;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.springframework.beans.factory.InitializingBean;
22  import org.springframework.context.ResourceLoaderAware;
23  import org.springframework.core.io.ContextResource;
24  import org.springframework.core.io.DefaultResourceLoader;
25  import org.springframework.core.io.FileSystemResource;
26  import org.springframework.core.io.Resource;
27  import org.springframework.core.io.ResourceLoader;
28  import org.springframework.core.io.support.ResourcePatternResolver;
29  import org.springframework.core.io.support.ResourcePatternUtils;
30  import org.springframework.util.Assert;
31  
32  import java.io.File;
33  import java.io.IOException;
34  import java.util.ArrayList;
35  import java.util.Arrays;
36  import java.util.Collections;
37  import java.util.List;
38  
39  /**
40   * An implementation of {@link FileService} that deals with files only in the
41   * local file system. Files and triggers are created in subdirectories of the
42   * Java temporary directory.
43   * 
44   * @author Dave Syer
45   * 
46   */
47  public class LocalFileService implements FileService, InitializingBean, ResourceLoaderAware {
48  
49  	private File outputDir = new File(System.getProperty("java.io.tmpdir", "/tmp"), "batch/files");
50  
51  	private static final Log logger = LogFactory.getLog(LocalFileService.class);
52  
53  	private ResourceLoader resourceLoader = new DefaultResourceLoader();
54  
55  	private FileSender fileSender;
56  
57  	public void setResourceLoader(ResourceLoader resourceLoader) {
58  		this.resourceLoader = resourceLoader;
59  	}
60  
61  	public void setFileSender(FileSender fileSender) {
62  		this.fileSender = fileSender;
63  	}
64  
65  	/**
66  	 * The output directory to store new files. Defaults to
67  	 * <code>${java.io.tmpdir}/batch/files</code>.
68  	 * @param outputDir the output directory to set
69  	 */
70  	public void setOutputDir(File outputDir) {
71  		this.outputDir = outputDir;
72  	}
73  
74  	public void afterPropertiesSet() throws Exception {
75  		Assert.state(fileSender != null, "A FileSender must be provided");
76  		if (!outputDir.exists()) {
77  			Assert.state(outputDir.mkdirs(), "Cannot create output directory " + outputDir);
78  		}
79  		Assert.state(outputDir.exists(), "Output directory does not exist " + outputDir);
80  		Assert.state(outputDir.isDirectory(), "Output file is not a directory " + outputDir);
81  	}
82  
83  	public FileInfo createFile(String path) throws IOException {
84  
85  		path = sanitize(path);
86  
87  		Assert.hasText(path, "The file path must not be empty");
88  
89  		String name = path.substring(path.lastIndexOf("/") + 1);
90  		String parent = path.substring(0, path.lastIndexOf(name));
91  		if (parent.endsWith("/")) {
92  			parent = parent.substring(0, parent.length() - 1);
93  		}
94  
95  		File directory = new File(outputDir, parent);
96  		directory.mkdirs();
97  		Assert.state(directory.exists() && directory.isDirectory(), "Could not create directory: " + directory);
98  
99  		FileInfo result = new FileInfo(path);
100 		File dest = new File(outputDir, result.getFileName());
101 		dest.createNewFile();
102 
103 		return result;
104 
105 	}
106 
107 	/**
108 	 * @param target the target file
109 	 * @return the path to the file from the base output directory
110 	 */
111 	private String extractPath(File target) {
112 		String outputPath = outputDir.getAbsolutePath();
113 		return target.getAbsolutePath().substring(outputPath.length() + 1).replace("\\", "/");
114 	}
115 
116 	public boolean publish(FileInfo dest) throws IOException {
117 		String path = dest.getPath();
118 		fileSender.send(getResource(path).getFile());
119 		return true;
120 	}
121 
122 	public int countFiles() {
123 		ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
124 		Resource[] resources;
125 		try {
126 			resources = resolver.getResources("file:///" + outputDir.getAbsolutePath() + "/**");
127 		}
128 		catch (IOException e) {
129 			throw new IllegalStateException("Unexpected problem resolving files", e);
130 		}
131 		return resources.length;
132 	}
133 
134 	public List<FileInfo> getFiles(int startFile, int pageSize) throws IOException {
135 
136 		List<FileInfo> files = getFiles("**");
137 
138 		String path = "";
139 		int count = 0;
140 		for (FileInfo info : files) {
141 			FileInfo shortInfo = info.shortPath();
142 			if (!path.equals(shortInfo.getPath())) {
143 				files.set(count, shortInfo);
144 				path = shortInfo.getPath();
145 			}
146 			count++;
147 		}
148 
149 		return new ArrayList<FileInfo>(files.subList(startFile, Math.min(startFile + pageSize, files.size())));
150 
151 	}
152 
153 	private List<FileInfo> getFiles(String pattern) {
154 		ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
155 		List<Resource> resources = new ArrayList<Resource>();
156 
157 		if (!pattern.startsWith("/")) {
158 			pattern = "/" + outputDir.getAbsolutePath() + "/" + pattern;
159 		}
160 		if (!pattern.startsWith("file:")) {
161 			pattern = "file:///" + pattern;
162 		}
163 
164 		try {
165 			resources = Arrays.asList(resolver.getResources(pattern));
166 		}
167 		catch (IOException e) {
168 			logger.debug("Cannot locate files " + pattern, e);
169 			return new ArrayList<FileInfo>();
170 		}
171 
172 		List<FileInfo> files = new ArrayList<FileInfo>();
173 		for (Resource resource : resources) {
174 			File file;
175 			try {
176 				file = resource.getFile();
177 				if (file.isFile()) {
178 					FileInfo info = new FileInfo(extractPath(file));
179 					files.add(info);
180 				}
181 			}
182 			catch (IOException e) {
183 				logger.debug("Cannot locate file " + resource, e);
184 			}
185 		}
186 
187 		Collections.sort(files);
188 		return new ArrayList<FileInfo>(files);
189 
190 	}
191 
192 	public int delete(String pattern) throws IOException {
193 
194 		ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
195 		if (!pattern.startsWith("/")) {
196 			pattern = "/" + outputDir.getAbsolutePath() + "/" + pattern;
197 		}
198 		if (!pattern.startsWith("file:")) {
199 			pattern = "file:///" + pattern;
200 		}
201 
202 		Resource[] resources = resolver.getResources(pattern);
203 
204 		int count = 0;
205 		for (Resource resource : resources) {
206 			File file = resource.getFile();
207 			if (file.isFile()) {
208 				count++;
209 				FileUtils.deleteQuietly(file);
210 			}
211 		}
212 
213 		return count;
214 
215 	}
216 
217 	public Resource getResource(String path) {
218 
219 		path = sanitize(path);
220 		FileInfo pattern = new FileInfo(path);
221 		List<FileInfo> files = getFiles(pattern.getPattern());
222 		FileInfo info = files.isEmpty() ? pattern : files.get(0);
223 		File file = new File(outputDir, info.getFileName());
224 		return new FileServiceResource(file, path);
225 
226 	}
227 
228 	public File getUploadDirectory() {
229 		return outputDir;
230 	}
231 
232 	/**
233 	 * Normalize file separators to "/" and strip leading prefix and separators
234 	 * to create a simple relative path.
235 	 * 
236 	 * @param path the raw path
237 	 * @return a sanitized version
238 	 */
239 	private String sanitize(String path) {
240 		path = path.replace("\\", "/");
241 		if (path.startsWith("files:")) {
242 			path = path.substring("files:".length());
243 			while (path.startsWith("/")) {
244 				path = path.substring(1);
245 			}
246 		}
247 		return path;
248 	}
249 
250 	private static class FileServiceResource extends FileSystemResource implements ContextResource {
251 
252 		private final String path;
253 
254 		public FileServiceResource(File file, String path) {
255 			super(file);
256 			this.path = path;
257 		}
258 
259 		public String getPathWithinContext() {
260 			return path;
261 		}
262 
263 	}
264 
265 }