View Javadoc

1   /**
2    *  MicroEmulator
3    *  Copyright (C) 2001-2007 Bartek Teodorczyk <barteo@barteo.net>
4    *
5    *  It is licensed under the following two licenses as alternatives:
6    *    1. GNU Lesser General Public License (the "LGPL") version 2.1 or any newer version
7    *    2. Apache License (the "AL") Version 2.0
8    *
9    *  You may not use this file except in compliance with at least one of
10   *  the above two licenses.
11   *
12   *  You may obtain a copy of the LGPL at
13   *      http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
14   *
15   *  You may obtain a copy of the AL at
16   *      http://www.apache.org/licenses/LICENSE-2.0
17   *
18   *  Unless required by applicable law or agreed to in writing, software
19   *  distributed under the License is distributed on an "AS IS" BASIS,
20   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21   *  See the LGPL or the AL for the specific language governing permissions and
22   *  limitations.
23   */
24  
25  package org.microemu.app.util;
26  
27  import java.io.DataInputStream;
28  import java.io.DataOutputStream;
29  import java.io.File;
30  import java.io.FileInputStream;
31  import java.io.FileNotFoundException;
32  import java.io.FileOutputStream;
33  import java.io.FilenameFilter;
34  import java.io.IOException;
35  import java.security.AccessControlContext;
36  import java.security.AccessController;
37  import java.security.PrivilegedActionException;
38  import java.security.PrivilegedExceptionAction;
39  import java.util.Hashtable;
40  import java.util.Iterator;
41  import java.util.List;
42  import java.util.Vector;
43  
44  import javax.microedition.rms.RecordStore;
45  import javax.microedition.rms.RecordStoreException;
46  import javax.microedition.rms.RecordStoreNotFoundException;
47  import javax.microedition.rms.RecordStoreNotOpenException;
48  
49  import org.microemu.MicroEmulator;
50  import org.microemu.RecordStoreManager;
51  import org.microemu.app.Config;
52  import org.microemu.log.Logger;
53  import org.microemu.util.ExtendedRecordListener;
54  import org.microemu.util.RecordStoreImpl;
55  
56  public class FileRecordStoreManager implements RecordStoreManager {
57  
58  	private final static String RECORD_STORE_SUFFIX = ".rs";
59  
60  	private final static List replaceChars = new Vector();
61  
62  	private MicroEmulator emulator;
63  
64  	private Hashtable testOpenRecordStores = new Hashtable();
65  
66  	private ExtendedRecordListener recordListener = null;
67  
68  	/* The context to be used when accessing files in Webstart */
69  	private AccessControlContext acc;
70  
71  	static {
72  		replaceChars.add(":");
73  		replaceChars.add("*");
74  		replaceChars.add("?");
75  		replaceChars.add("=");
76  		replaceChars.add("|");
77  		replaceChars.add("/");
78  		replaceChars.add("\\");
79  		replaceChars.add("\"");
80  	}
81  
82  	private FilenameFilter filter = new FilenameFilter() {
83  		public boolean accept(File dir, String name) {
84  			if (name.endsWith(RECORD_STORE_SUFFIX)) {
85  				return true;
86  			} else {
87  				return false;
88  			}
89  		}
90  	};
91  
92  	public void init(MicroEmulator emulator) {
93  		this.emulator = emulator;
94  		this.acc = AccessController.getContext();
95  	}
96  
97  	public String getName() {
98  		return "File record store";
99  	}
100 
101 	private File getSuiteFolder() {
102 		return new File(Config.getConfigPath(), "suite-" + emulator.getLauncher().getSuiteName());
103 	}
104 
105 	private static String escapeCharacter(String charcter) {
106 		return "_%%" + (int) (charcter.charAt(0)) + "%%_";
107 	}
108 
109 	static String recordStoreName2FileName(String recordStoreName) {
110 		for (Iterator iterator = replaceChars.iterator(); iterator.hasNext();) {
111 			String c = (String) iterator.next();
112 			String newValue = escapeCharacter(c);
113 			if (c.equals("\\")) {
114 				c = "\\\\";
115 			}
116 			c = "[" + c + "]";
117 			recordStoreName = recordStoreName.replaceAll(c, newValue);
118 		}
119 		return recordStoreName + RECORD_STORE_SUFFIX;
120 	}
121 
122 	static String fileName2RecordStoreName(String fileName) {
123 		for (Iterator iterator = replaceChars.iterator(); iterator.hasNext();) {
124 			String c = (String) iterator.next();
125 			String newValue = escapeCharacter(c);
126 			if (c.equals("\\")) {
127 				c = "\\\\";
128 			}
129 			fileName = fileName.replaceAll(newValue, c);
130 		}
131 		return fileName.substring(0, fileName.length() - RECORD_STORE_SUFFIX.length());
132 	}
133 
134 	public void deleteRecordStore(final String recordStoreName) throws RecordStoreNotFoundException,
135 			RecordStoreException {
136 		final File storeFile = new File(getSuiteFolder(), recordStoreName2FileName(recordStoreName));
137 
138 		RecordStoreImpl recordStoreImpl = (RecordStoreImpl) testOpenRecordStores.get(storeFile.getName());
139 		if (recordStoreImpl != null && recordStoreImpl.isOpen()) {
140 			throw new RecordStoreException();
141 		}
142 
143 		try {
144 			recordStoreImpl = loadFromDisk(storeFile);
145 		} catch (FileNotFoundException ex) {
146 			throw new RecordStoreNotFoundException(recordStoreName);
147 		}
148 
149 		try {
150 			AccessController.doPrivileged(new PrivilegedExceptionAction() {
151 				public Object run() throws FileNotFoundException {
152 					storeFile.delete();
153 					fireRecordStoreListener(ExtendedRecordListener.RECORDSTORE_DELETE, recordStoreName);
154 					return null;
155 				}
156 			}, acc);
157 		} catch (PrivilegedActionException e) {
158 			Logger.error("Unable remove file " + storeFile, e);
159 			throw new RecordStoreException();
160 		}
161 	}
162 
163 	public RecordStore openRecordStore(String recordStoreName, boolean createIfNecessary) throws RecordStoreException {
164 		File storeFile = new File(getSuiteFolder(), recordStoreName2FileName(recordStoreName));
165 
166 		RecordStoreImpl recordStoreImpl;
167 		try {
168 			recordStoreImpl = loadFromDisk(storeFile);
169 		} catch (FileNotFoundException e) {
170 			if (!createIfNecessary) {
171 				throw new RecordStoreNotFoundException(recordStoreName);
172 			}
173 			recordStoreImpl = new RecordStoreImpl(this, recordStoreName);
174 			saveToDisk(storeFile, recordStoreImpl);
175 		}
176 		recordStoreImpl.setOpen(true);
177 		if (recordListener != null) {
178 			recordStoreImpl.addRecordListener(recordListener);
179 		}
180 
181 		testOpenRecordStores.put(storeFile.getName(), recordStoreImpl);
182 
183 		fireRecordStoreListener(ExtendedRecordListener.RECORDSTORE_OPEN, recordStoreName);
184 
185 		return recordStoreImpl;
186 	}
187 
188 	public String[] listRecordStores() {
189 		String[] result;
190 		try {
191 			result = (String[]) AccessController.doPrivileged(new PrivilegedExceptionAction() {
192 				public Object run() {
193 					return getSuiteFolder().list(filter);
194 				}
195 			}, acc);
196 		} catch (PrivilegedActionException e) {
197 			Logger.error("Unable to access storeFiles", e);
198 			return null;
199 		}
200 		if (result != null) {
201 			if (result.length == 0) {
202 				result = null;
203 			} else {
204 				for (int i = 0; i < result.length; i++) {
205 					result[i] = fileName2RecordStoreName(result[i]);
206 				}
207 			}
208 		}
209 		return result;
210 	}
211 
212 	public void saveChanges(RecordStoreImpl recordStoreImpl) throws RecordStoreNotOpenException, RecordStoreException {
213 
214 		File storeFile = new File(getSuiteFolder(), recordStoreName2FileName(recordStoreImpl.getName()));
215 
216 		saveToDisk(storeFile, recordStoreImpl);
217 	}
218 
219 	public void init() {
220 	}
221 
222 	public void deleteStores() {
223 		String[] stores = listRecordStores();
224 		for (int i = 0; i < stores.length; i++) {
225 			String store = stores[i];
226 			try {
227 				deleteRecordStore(store);
228 			} catch (RecordStoreException e) {
229 				Logger.debug("deleteRecordStore", e);
230 			}
231 		}
232 	}
233 
234 	private RecordStoreImpl loadFromDisk(final File recordStoreFile) throws FileNotFoundException {
235 		try {
236 			return (RecordStoreImpl) AccessController.doPrivileged(new PrivilegedExceptionAction() {
237 				public Object run() throws FileNotFoundException {
238 					return loadFromDiskSecure(recordStoreFile);
239 				}
240 			}, acc);
241 		} catch (PrivilegedActionException e) {
242 			if (e.getCause() instanceof FileNotFoundException) {
243 				throw (FileNotFoundException) e.getCause();
244 			}
245 			Logger.error("Unable access file " + recordStoreFile, e);
246 			throw new FileNotFoundException();
247 		}
248 	}
249 
250 	private RecordStoreImpl loadFromDiskSecure(File recordStoreFile) throws FileNotFoundException {
251 		RecordStoreImpl store = null;
252 		try {
253 			DataInputStream dis = new DataInputStream(new FileInputStream(recordStoreFile));
254 			store = new RecordStoreImpl(this, dis);
255 			dis.close();
256 		} catch (FileNotFoundException e) {
257 			throw e;
258 		} catch (IOException e) {
259 			Logger.error("RecordStore.loadFromDisk: ERROR reading " + recordStoreFile.getName(), e);
260 		}
261 		return store;
262 	}
263 
264 	private void saveToDisk(final File recordStoreFile, final RecordStoreImpl recordStore) throws RecordStoreException {
265 		try {
266 			AccessController.doPrivileged(new PrivilegedExceptionAction() {
267 				public Object run() throws RecordStoreException {
268 					saveToDiskSecure(recordStoreFile, recordStore);
269 					return null;
270 				}
271 			}, acc);
272 		} catch (PrivilegedActionException e) {
273 			if (e.getCause() instanceof RecordStoreException) {
274 				throw (RecordStoreException) e.getCause();
275 			}
276 			Logger.error("Unable access file " + recordStoreFile, e);
277 			throw new RecordStoreException();
278 		}
279 	}
280 
281 	private void saveToDiskSecure(final File recordStoreFile, final RecordStoreImpl recordStore)
282 			throws RecordStoreException {
283 		if (!recordStoreFile.getParentFile().exists()) {
284 			if (!recordStoreFile.getParentFile().mkdirs()) {
285 				throw new RecordStoreException("Unable to create recordStore directory");
286 			}
287 		}
288 		try {
289 			DataOutputStream dos = new DataOutputStream(new FileOutputStream(recordStoreFile));
290 			recordStore.write(dos);
291 			dos.close();
292 		} catch (IOException e) {
293 			Logger.error("RecordStore.saveToDisk: ERROR writting object to " + recordStoreFile.getName(), e);
294 			throw new RecordStoreException(e.getMessage());
295 		}
296 	}
297 
298 	public int getSizeAvailable(RecordStoreImpl recordStoreImpl) {
299 		// FIXME should return free space on device
300 		return 1024 * 1024;
301 	}
302 
303 	public void setRecordListener(ExtendedRecordListener recordListener) {
304 		this.recordListener = recordListener;
305 	}
306 
307 	public void fireRecordStoreListener(int type, String recordStoreName) {
308 		if (recordListener != null) {
309 			recordListener.recordStoreEvent(type, System.currentTimeMillis(), recordStoreName);
310 		}
311 	}
312 }