001: /*
002: * Copyright (C) 2005 Jeff Tassin
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package com.jeta.swingbuilder.app;
020:
021: import java.io.ByteArrayInputStream;
022: import java.io.ByteArrayOutputStream;
023: import java.io.File;
024: import java.io.FileNotFoundException;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.ObjectInputStream;
028: import java.io.ObjectOutputStream;
029: import java.io.OutputStream;
030: import java.util.HashMap;
031: import java.util.Iterator;
032:
033: import com.jeta.open.registry.JETARegistry;
034: import com.jeta.swingbuilder.interfaces.app.ObjectStore;
035: import com.jeta.swingbuilder.interfaces.resources.ResourceLoader;
036:
037: /**
038: * This class implements the ObjectStore interface. It is used to manage
039: * serialization of object properties. The idea is to decouple application
040: * components from where we are serializing the data. We store the data in two
041: * directories: home/data and home/data/work The work directory is used to
042: * temporarily write out the store. If an error occurs, the user can still
043: * recover from the old state. If the write is successful to the work area, then
044: * we re-write to the data area.
045: *
046: * @author Jeff Tassin
047: */
048: public class ApplicationStateStore implements ObjectStore {
049: private HashMap m_objects; // the set of objects
050:
051: /** the directory where we store all of our objects */
052: private String m_directory;
053:
054: /** the directory we use as a work area */
055: private String m_workdirectory;
056:
057: public static final String PARENT_DIRECTORY = "data";
058:
059: /**
060: * An 'empty' object
061: */
062: private Object EMPTY_OBJECT = new Object();
063:
064: /**
065: * Flag that indicates if store is readonl
066: */
067: private boolean m_readonly = false;
068:
069: /**
070: * ctor
071: */
072: public ApplicationStateStore(String directory) {
073: m_directory = directory;
074: m_workdirectory = m_directory + File.separatorChar + "temp";
075: m_objects = new HashMap();
076: }
077:
078: /**
079: * Deletes the object from the store cache as well as on disk
080: */
081: private synchronized void _delete(String keyName, boolean btemp)
082: throws IOException {
083: if (isReadOnly())
084: return;
085:
086: try {
087: ResourceLoader loader = (ResourceLoader) JETARegistry
088: .lookup(ResourceLoader.COMPONENT_ID);
089: String resource = getTargetDirectory(btemp)
090: + File.separatorChar + keyName;
091: loader.deleteResource(resource);
092: } catch (IOException io) {
093: // eat here
094: }
095: }
096:
097: /**
098: * Deletes the object from the store cache as well as on disk
099: */
100: public synchronized void delete(String keyName) throws IOException {
101: _delete(keyName, true);
102: _delete(keyName, false);
103: m_objects.remove(keyName);
104: }
105:
106: /**
107: * Saves the entire store to disk
108: */
109: public synchronized void flush() throws IOException {
110: save();
111: }
112:
113: /**
114: * Saves the specified object to disk
115: */
116: private synchronized void _flush(String keyName, boolean btemp)
117: throws IOException {
118: if (isReadOnly())
119: return;
120:
121: ResourceLoader loader = (ResourceLoader) JETARegistry
122: .lookup(ResourceLoader.COMPONENT_ID);
123: // make sure the directory exists
124: String targetdir = getTargetDirectory(btemp);
125: loader.createSubdirectories(targetdir);
126:
127: Object obj = m_objects.get(keyName);
128: if (obj != null && obj != EMPTY_OBJECT) {
129: // we write each object to its own file
130: OutputStream ostream = loader.getOutputStream(targetdir
131: + File.separatorChar + keyName);
132:
133: byte[] data = (byte[]) obj;
134: ostream.write(data);
135: ostream.flush();
136: ostream.close();
137: }
138: }
139:
140: /**
141: * Saves the specified object to disk
142: */
143: public synchronized void flush(String keyName) throws IOException {
144: // flush to temp area to verify there are no problems
145: _flush(keyName, true);
146: // ok, if succesful, the flush the object to the actual area
147: _flush(keyName, false);
148: }
149:
150: /**
151: * @return the relative path to the directory where we store our objects
152: */
153: public String getTargetDirectory() {
154: return getTargetDirectory(false);
155: }
156:
157: /**
158: * @return the relative path to the directory where we store our objects
159: */
160: public String getTargetDirectory(boolean temp) {
161: if (temp)
162: return PARENT_DIRECTORY + File.separatorChar
163: + m_workdirectory;
164: else
165: return PARENT_DIRECTORY + File.separatorChar + m_directory;
166: }
167:
168: /**
169: * @return true if the store is readonly.
170: */
171: public boolean isReadOnly() {
172: return m_readonly;
173: }
174:
175: /**
176: * ObjectStore implementation
177: */
178: public synchronized Object load(String keyName) throws IOException {
179: if (m_objects == null)
180: return null;
181: else {
182: Object obj = m_objects.get(keyName);
183: if (obj == null) {
184: obj = loadObject(keyName);
185: if (obj != null)
186: m_objects.put(keyName, obj);
187:
188: }
189:
190: if (obj == EMPTY_OBJECT)
191: obj = null;
192:
193: /**
194: * all objects are currently stored as byte arrays, so we need to
195: * deserialize from the byte array to an actual object
196: */
197: if (obj != null) {
198: byte[] data = (byte[]) obj;
199: ByteArrayInputStream bais = new ByteArrayInputStream(
200: data);
201: ObjectInputStream ois = new ObjectInputStream(bais);
202: try {
203: obj = ois.readObject();
204: } catch (ClassNotFoundException cnfe) {
205: IOException ioe = new IOException(cnfe
206: .getLocalizedMessage());
207: throw ioe;
208: }
209: }
210: return obj;
211: }
212: }
213:
214: /**
215: * loads the named object from the store. This object is also deserialized
216: * at this point since the store only keeps track of serialized data
217: *
218: * @param keyName
219: * the unique name of the lobject to retrieve
220: * @param defaultValue
221: * the value to return if the name is not found in the store
222: * @return the instantiated object from the store. The default value is
223: * returned if the name is not found.
224: * @throws IOException
225: * if an error occurs during deserialization
226: */
227: public Object load(String keyName, Object defaultValue)
228: throws IOException {
229: Object result = load(keyName);
230: if (result == null)
231: result = defaultValue;
232: return result;
233: }
234:
235: /**
236: * Loads a named object from the state store and puts it in the cache
237: */
238: private synchronized Object loadObject(String keyName) {
239: if (isReadOnly())
240: return null;
241:
242: Object result = null;
243: ResourceLoader loader = (ResourceLoader) JETARegistry
244: .lookup(ResourceLoader.COMPONENT_ID);
245:
246: // make sure the directory exists
247: try {
248: String targetdir = getTargetDirectory();
249: InputStream istream = loader.getInputStream(targetdir
250: + File.separatorChar + keyName);
251: if (istream != null) {
252: ByteArrayOutputStream bos = new ByteArrayOutputStream();
253: byte[] buff = new byte[1024];
254: int numread = istream.read(buff);
255: while (numread > 0) {
256: bos.write(buff, 0, numread);
257: numread = istream.read(buff);
258: }
259: byte[] data = bos.toByteArray();
260:
261: istream.close();
262: result = data;
263:
264: }
265: return result;
266: } catch (FileNotFoundException fnfe) {
267: result = EMPTY_OBJECT;
268: } catch (Exception e) {
269: e.printStackTrace();
270: }
271:
272: return result;
273: }
274:
275: /**
276: * Writes out the data to disk
277: */
278: private synchronized void _save(boolean btemp) throws IOException {
279: if (isReadOnly())
280: return;
281:
282: ResourceLoader loader = (ResourceLoader) JETARegistry
283: .lookup(ResourceLoader.COMPONENT_ID);
284: // make sure the directory exists
285: String targetdir = getTargetDirectory(btemp);
286: loader.createSubdirectories(targetdir);
287:
288: Iterator iter = m_objects.keySet().iterator();
289: while (iter.hasNext()) {
290: String objectname = (String) iter.next();
291:
292: Object obj = m_objects.get(objectname);
293: if (obj != null && obj != EMPTY_OBJECT) {
294: // we write each object to its own file
295: OutputStream ostream = loader.getOutputStream(targetdir
296: + File.separatorChar + objectname);
297: byte[] data = (byte[]) obj;
298: ostream.write(data);
299: ostream.flush();
300: ostream.close();
301: }
302: }
303: }
304:
305: /**
306: * Writes out the data to disk
307: */
308: public synchronized void save() throws IOException {
309: // save to temp area to verify there are no problems
310: _save(true);
311: // ok, if succesful, save the object to the actual area
312: _save(false);
313: }
314:
315: /**
316: * Sets the flag that indicates if the store is readonly.
317: */
318: public void setReadOnly(boolean readonly) {
319: m_readonly = readonly;
320: }
321:
322: /**
323: * ObjectStore implementation
324: */
325: public synchronized void store(String keyName, Object obj)
326: throws IOException {
327: ByteArrayOutputStream baos = new ByteArrayOutputStream();
328: ObjectOutputStream s = new ObjectOutputStream(baos);
329: s.writeObject(obj);
330: s.flush();
331: m_objects.put(keyName, baos.toByteArray());
332: }
333:
334: }
|