001: /*=============================================================================
002: * Copyright Texas Instruments 2002. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * This program 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
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package ti.chimera.registry;
020:
021: import ti.exceptions.ProgrammingErrorException;
022:
023: import oscript.fs.AbstractFileSystem;
024: import oscript.fs.AbstractFile;
025: import oscript.fs.JarFileSystem;
026:
027: import java.io.*;
028:
029: /**
030: * A node that persistently stores its value. When a persistent node is
031: * first linked in to the tree, it checks for a previously stored value, as
032: * identified by the primary path (ie. the first path the node is linked
033: * to) and if a previously stored value is found, it overrides the initial
034: * value specified in the constructor. The value of a persistent node must
035: * be {@link java.io.Serializable}.
036: * <p>
037: * Note that currently values of a persistent node are stored when they are
038: * set, rather than at system exit, so the stored value won't reflect side-
039: * effects... for example if the value is a list, and an element is added
040: * to the list after the value of the node gets set, the stored value won't
041: * reflect the element added to the list. This is deemed to be the correct
042: * behavior because it is consistent with the registry in that values of
043: * registry nodes are only published to subscribers when they are changed
044: * (via {@link #setValue}).
045: * <p>
046: * Description of properties that are interesting:
047: * <table>
048: * <tr>
049: * <th>property</th>
050: * <th>description</th>
051: * <th>regular default</th>
052: * <th>webstart default</th>
053: * </tr>
054: * <tr>
055: * <td>chimera.config.path</td>
056: * <td>where the persistent registry nodes are stored</th>
057: * <td>$CWD/config.jar</td>
058: * <td>$HOME/.config.jar</td>
059: * </tr>
060: * </table>
061: *
062: * @author ;Rob Clark;a0873619;San Diego;;
063: * @version 0.1
064: */
065: public class PersistentNode extends Node {
066: /**
067: * The first path that this node gets linked to, which is used as part
068: * of the path to the serialized value
069: */
070: private String persistentPath;
071:
072: static {
073: // mount config.jar at /config
074: try {
075: String path = "true".equals(System
076: .getProperty("oscript.webstart")) ? System
077: .getProperty("user.home")
078: + "/.config.jar" : "config.jar";
079: path = System.getProperty("chimera.config.path", path);
080: System.setProperty("chimera.config.path", path);
081: AbstractFileSystem
082: .mount(new JarFileSystem(path), "/config");
083: } catch (Exception e) {
084: throw new ProgrammingErrorException(e);
085: }
086: }
087:
088: /**
089: * Class Constructor.
090: *
091: * @param value the node's initial value
092: * @param contract the node's contract, or <code>null</code>
093: * @param comment a string containing a description of the purpose
094: * of this node, the node's usage, etc. Can contain HTML markup.
095: */
096: public PersistentNode(Object value, NodeContract contract,
097: String comment) {
098: super (value, contract, comment);
099: }
100:
101: /**
102: * Set the value of this node, and publish the new value to all the
103: * subscribers.
104: *
105: * @param value the node's new value
106: */
107: public void setValue(Object value) {
108: if ((value != null) && !(value instanceof Serializable))
109: throw new ProgrammingErrorException("value (" + value
110: + ") is not serializable");
111:
112: if (persistentPath != null) {
113: try {
114: AbstractFile file = AbstractFileSystem
115: .resolve("/config/" + persistentPath + ".dat");
116:
117: if (!file.exists())
118: file.createNewFile();
119:
120: ObjectOutputStream oos = new ObjectOutputStream(
121: new BufferedOutputStream(file
122: .getOutputStream(false)));
123:
124: oos.writeObject(value);
125: oos.flush();
126: oos.close();
127: } catch (IOException e) {
128: throw new ProgrammingErrorException(e);
129: }
130: }
131:
132: super .setValue(value);
133: }
134:
135: /**
136: * Called by the registry before this node is linked in to the tree. This
137: * should not be called anywhere else, otherwise undefined behaviour may
138: * ensue. (Translation: <i>Nothing to see here, move along.</i>).
139: */
140: void link(String path) {
141: if (persistentPath == null) {
142: persistentPath = path;
143:
144: try {
145: AbstractFile file = AbstractFileSystem
146: .resolve("/config/" + persistentPath + ".dat");
147:
148: if (file.exists()) {
149: ObjectInputStream ois = new ObjectInputStream(file
150: .getInputStream());
151: Object obj = ois.readObject();
152: // XXX work around bug on java 1.4.1_01-69.1 on macosx
153: if (obj instanceof java.awt.Font) {
154: java.awt.Font f = (java.awt.Font) obj;
155: // obj = new java.awt.Font( f.getAttributes() ); <-- DOESN'T WORK
156: obj = new java.awt.Font(f.getName(), f
157: .getStyle(), f.getSize());
158: }
159: if (getNodeContract().accepts(obj))
160: setValue(obj);
161: }
162: } catch (ClassNotFoundException e) {
163: // silently ignore if we can't load stored value
164: } catch (IOException e) {
165: // silently ignore if we can't load stored value
166: } catch (ProgrammingErrorException e) {
167: // silently ignore if stored value does not match constract
168: }
169: }
170:
171: super .link(path);
172: }
173: }
174:
175: /*
176: * Local Variables:
177: * tab-width: 2
178: * indent-tabs-mode: nil
179: * mode: java
180: * c-indentation-style: java
181: * c-basic-offset: 2
182: * eval: (c-set-offset 'substatement-open '0)
183: * eval: (c-set-offset 'case-label '+)
184: * eval: (c-set-offset 'inclass '+)
185: * eval: (c-set-offset 'inline-open '0)
186: * End:
187: */
|