View Javadoc

1   package pl.psnc.dl.ege.utils;
2   
3   import java.io.ByteArrayInputStream;
4   import java.io.File;
5   import java.io.FileInputStream;
6   import java.io.FileNotFoundException;
7   import java.io.IOException;
8   import java.io.InputStream;
9   import java.io.OutputStream;
10  import java.util.HashMap;
11  import java.util.Map;
12  import java.util.UUID;
13  
14  import org.apache.commons.io.FileCleaningTracker;
15  import org.apache.log4j.Logger;
16  
17  import pl.psnc.dl.ege.EGEImpl;
18  
19  /**
20   * Keeps collection of buffered byte data, which is used mainly by conversion
21   * with validation process.<br/><br/> 
22   * 
23   * Each singular data item can be kept in memory - if it does not reaches specified
24   * threshold (in number of bytes). If it does DataBuffer is permitted to save data
25   * as temporary file.<br/><br/>
26   * 
27   * <b>Important:</b> while buffer is referenced it is not cleared from data;
28   * assigning new items indefinitely may result in stack overflow.<br/>
29   * Memory can be relieved by class methods.<br/>
30   * 
31   * Each allocation in buffer is of type : single-write/multiple-read.<br/>
32   * 
33   *    
34   * @author mariuszs
35   *
36   */
37  /*
38   * TODO : Przerobki - nie zwracac 'id' alokacji, ale Item. Pozwolic na wielokrotny zapis (?).
39   */
40  public class DataBuffer
41  {
42  
43  	private static final Logger LOGGER = Logger.getLogger(DataBuffer.class);
44  
45  	/**
46  	 * Default value : max size of item (in number of bytes), that allows to keep it in memory.
47  	 */
48  	public static final int DEFAULT_ITEM_MAX_SIZE = 102400;
49  
50  	/*
51  	 * List of buffered items
52  	 */
53  	private Map<String, Item> items = new HashMap<String, Item>();
54  
55  	/*
56  	 * Maximum size of data item (in number of bytes)
57  	 */
58  	private int itemMaxSize = DEFAULT_ITEM_MAX_SIZE;
59  
60  	/*
61  	 * Directory for temporary files.
62  	 */
63  	private String tmpDirectory;
64  
65  	/*
66  	 * Tracker of temporary files - which are deleted, when reference to data item is dropped.
67  	 */
68  	private final FileCleaningTracker tracker = new FileCleaningTracker();
69  
70  
71  	/**
72  	 * Creates instance of data buffer with specified temporary files directory
73  	 * - where overwhelmed data is kept. 
74  	 * 
75  	 * @param tmpDirectory temporary files directory
76  	 */
77  	public DataBuffer(String tmpDirectory)
78  	{
79  		this.tmpDirectory = tmpDirectory;
80  		File dir = new File(tmpDirectory);
81  		if (!dir.exists()) {
82  			dir.mkdir();
83  		}
84  	}
85  
86  
87  	/**
88  	 * Creates instance of data buffer with specified temporary files directory
89  	 * and threshold for every contained data item. 
90  	 * 
91  	 * @param itemMaxSize maximum size of data item
92  	 * @param tmpDirectory temporary files directory
93  	 */
94  	public DataBuffer(int itemMaxSize, String tmpDirectory)
95  	{
96  		this(tmpDirectory);
97  		this.itemMaxSize = itemMaxSize;
98  	}
99  
100 
101 	/**
102 	 * <p>Allocates clean data item in buffer.</p>
103 	 * Method returns id of allocated data item.  
104 	 * 
105 	 * @return 'id' of allocated data item.
106 	 */
107 	public String allocate()
108 	{
109 		String id = UUID.randomUUID().toString();
110 		Item item = new Item(id);
111 		items.put(id, item);
112 		tracker.track(item.getFile(), item);
113 		return id;
114 	}
115 	
116 	/**
117 	 * <p>Returns buffer allocation output stream.</p> 
118 	 * If output stream of selected item was closed, using
119 	 * this method again on the same item will result in IllegalStateException.
120 	 * 
121 	 * @param id of buffer item
122 	 * @return buffer item output stream
123 	 * @throws IllegalStateException
124 	 */
125 	public OutputStream getElementOutputStream(String id) throws IllegalStateException {
126 		Item item = items.get(id);
127 		if(item.isCommited()){
128 			throw new IllegalStateException("Buffer element already filled.");
129 		}
130 		return item.getOutputStream();
131 	}
132 	
133 	/**
134 	 * Reads data from specified input stream and creates
135 	 * single data item.<br/> 
136 	 * If item maximum size is reached, data is written to temporary file.   
137 	 * Method returns unique id of created item.
138 	 * 
139 	 * @param inputStream streamed input data
140 	 * @return 'id' of allocated data item
141 	 */
142 	public String allocate(InputStream inputStream)
143 	{
144 		String id = UUID.randomUUID().toString();
145 		Item item = new Item(id);
146 		item.write(inputStream);
147 		items.put(id, item);
148 		tracker.track(item.getFile(), item);
149 		return id;
150 	}
151 	
152 	/**
153 	 * Reads data from specified input stream and creates
154 	 * single data item.<br> 
155 	 * <p>If item maximum size is reached, data is written to temporary file
156 	 * with name of 'itemName'.</p>   
157 	 * Method returns unique id of created item.
158 	 *  
159 	 * @param inputStream
160 	 * @param itemName
161 	 * @return
162 	 */
163 	public String allocate(InputStream inputStream, String itemName){
164 		String id = UUID.randomUUID().toString();
165 		Item item = new Item(id, itemName);
166 		item.write(inputStream);
167 		items.put(id, item);
168 		tracker.track(item.getFile(), item);
169 		return id;
170 	}
171 	
172 	/**
173 	 * <p>Returns specified by id - data item as input stream.</p>
174 	 * If item does not exists in buffer method returns 'null'.
175 	 *  
176 	 * @param 'id' of a data item.
177 	 * @return streamed data item
178 	 */
179 	public InputStream getDataAsStream(String id)
180 	{
181 		try {
182 			Item item = items.get(id);
183 			if (item != null) {
184 				return item.getStream();
185 			}
186 			return null;
187 		}
188 		catch (FileNotFoundException ex) {
189 			throw new RuntimeException("Temporary file generator exception!");
190 		}
191 
192 	}
193 	
194 	/**
195 	 * Returns temp dir of allocated item - can be null if item
196 	 * is stored in memory.
197 	 * 
198 	 * @param id
199 	 * @return
200 	 */
201 	public String getDataDir(String id){
202 		Item item = items.get(id);
203 		if(item!=null){
204 			return item.getDir();
205 		}
206 		return null;
207 	}
208 
209 	/**
210 	 * <p>Relieves selected data item.</p>
211 	 * If 'forceDelete' parameter is set to 'true' temporary file (if it was created)
212 	 * will be deleted immediately, otherwise it will be deleted after release 
213 	 * of memory by garbage collector.<br/>   
214 	 * Method returns 'false' if selected item does not exists in buffer, 
215 	 * otherwise it returns 'true'.
216 	 * 
217 	 * @param 'id' of data item
218 	 */
219 	public boolean removeData(String id, boolean forceDelete)
220 	{
221 		Item item = items.get(id);
222 		if (item == null)
223 			return false;
224 		if (forceDelete) {
225 			item.deleteDir();
226 		}
227 		items.remove(id);
228 		return true;
229 	}
230 
231 
232 	/**
233 	 * <p>Relieves all stored in buffer data items</p>
234 	 * If 'forceDelete' parameter is set to 'true' all temporary files 
235 	 * will be deleted immediately, otherwise they will be deleted
236 	 * after release of memory by garbage collector.
237 	 * 
238 	 */
239 	public void clear(boolean forceDelete)
240 	{
241 		if (forceDelete) {
242 			for (Item item : items.values()) {
243 				item.deleteDir();
244 			}
245 		}
246 		items = new HashMap<String,Item>();
247 	}
248 	
249 	public String getTemporaryDir(){
250 		return this.tmpDirectory;
251 	}
252 	
253 	/*
254 	 * Inner class : represents single item of data. 
255 	 */
256 	class Item
257 	{
258 
259 		private BufferOutputStream os;
260 
261 		private File tmpFile;
262 
263 		private String tmpDir;
264 		
265 		private boolean commited = false;
266 
267 
268 		public Item(String id)
269 		{
270 			this.tmpDir = tmpDirectory + File.separator + id;
271 			File tempDir = new File(tmpDir);
272 			tempDir.mkdir();
273 			this.tmpFile = new File(tmpDir + File.separator + "backup.ebu");
274 			os = new BufferOutputStream(itemMaxSize, tmpFile, this);
275 		}
276 		
277 		public Item(String id, String itemName){
278 			this.tmpDir = tmpDirectory + File.separator + id;
279 			File tempDir = new File(tmpDir);
280 			tempDir.mkdir();
281 			this.tmpFile = new File(tmpDir + File.separator + itemName);
282 			os = new BufferOutputStream(itemMaxSize, tmpFile, this);
283 		}
284 		
285 		public void write(InputStream is)
286 		{
287 			int b;
288 			byte[] buf = new byte[EGEImpl.BUFFER_SIZE];
289 			try {
290 				while ((b = is.read(buf)) != -1) {
291 					os.write(buf, 0, b);
292 				}
293 			}
294 			catch (IOException ex) {
295 				LOGGER.error(ex.getMessage());
296 			}
297 			finally {
298 				try {
299 					os.close();
300 				}
301 				catch (IOException ex) {
302 					LOGGER.error(ex.getMessage());
303 				}
304 			}
305 		}
306 
307 
308 		public InputStream getStream()
309 			throws FileNotFoundException
310 		{
311 			byte[] data = os.getData();
312 			if (data == null) {
313 				return new FileInputStream(os.getFile());
314 			}
315 			else {
316 				return new ByteArrayInputStream(data);
317 			}
318 		}
319 
320 
321 		/*
322 		 * Deletes temporary file
323 		 */
324 		public void deleteFile()
325 		{
326 			if (!os.isInMemory()) {
327 				if (os.getFile().exists()) {
328 					LOGGER.debug("Removing tmp file : " + os.getFile());
329 					os.getFile().delete();
330 				}
331 			}
332 		}
333 		
334 		public void deleteDir(){
335 			if (!os.isInMemory()) {
336 				File dir = new File(tmpDir);
337 				if(dir.exists()){
338 					EGEIOUtils.deleteDirectory(dir);
339 				}
340 			}
341 		}
342 		
343 		public OutputStream getOutputStream(){
344 			return os;
345 		}
346 
347 		public File getFile()
348 		{
349 			return tmpFile;
350 		}
351 		
352 		private String getDir(){
353 			return tmpDir;
354 		}
355 
356 		public boolean isCommited()
357 		{
358 			return Boolean.valueOf(commited);
359 		}
360 		
361 		public void commit(){
362 			this.commited = true;
363 		}
364 
365 	}
366 
367 }