001: /*
002: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License version
007: * 2 only, as published by the Free Software Foundation.
008: *
009: * This program is distributed in the hope that it will be useful, but
010: * WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * General Public License version 2 for more details (a copy is
013: * included at /legal/license.txt).
014: *
015: * You should have received a copy of the GNU General Public License
016: * version 2 along with this work; if not, write to the Free Software
017: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
018: * 02110-1301 USA
019: *
020: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
021: * Clara, CA 95054 or visit www.sun.com if you need additional
022: * information or have any questions.
023: */
024:
025: package com.sun.midp.jump.push.executive.persistence;
026:
027: import com.sun.jump.module.contentstore.JUMPData;
028: import com.sun.jump.module.contentstore.JUMPNode;
029: import java.io.IOException;
030: import java.util.HashMap;
031: import java.util.Iterator;
032: import java.util.Map;
033:
034: /**
035: * Manages storing information associated with an application suite.
036: *
037: * <p>The class is <em>not</em> thread-safe.</p>
038: *
039: * <p>
040: * IMPORTANT_NOTE: The clients of this class should ensure that content store
041: * manager passed into the constructor doesn't have exclusive lock when
042: * methods are invoked. Otherwise we'll face deadlocks.
043: * <p>
044: */
045: final class AppSuiteDataStore {
046: /*
047: * Typical memory footprint vs. performance choice:
048: * one can keep all the data in the content store only and reparse
049: * them on each invocation or keep data cache (the latter option
050: * was picked)
051: */
052:
053: /**
054: * Abstarcts data to string and back conversion.
055: */
056: static interface DataConverter {
057: /**
058: * Converts data into a string.
059: *
060: * @param data data to convert
061: *
062: * @return string representation
063: */
064: String dataToString(Object data);
065:
066: /**
067: * Converts a string into data.
068: *
069: * @param s string to convert
070: *
071: * @return data
072: */
073: Object stringToData(String s);
074: }
075:
076: /** JUMP content store manager to use. */
077: private final StoreOperationManager storeManager;
078:
079: /** Dir to store data in. */
080: private final String dir;
081:
082: /** Data converter to use. */
083: private final DataConverter dataConverter;
084:
085: /** Data for all app suites. */
086: private Map data;
087:
088: /**
089: * Creats an app data store and reads the data.
090: *
091: * @param storeManager JUMP content store manager to use
092: * @param dir dir to store data in
093: * @param dataConverter data converter to use
094: *
095: * @throws IOException if IO fails
096: */
097: AppSuiteDataStore(final StoreOperationManager storeManager,
098: final String dir, final DataConverter dataConverter)
099: throws IOException {
100: if (storeManager == null) {
101: throw new IllegalArgumentException("storeManager is null");
102: }
103: this .storeManager = storeManager;
104:
105: if (dataConverter == null) {
106: throw new IllegalArgumentException("dataConverter is null");
107: }
108: this .dataConverter = dataConverter;
109:
110: if (dir == null) {
111: throw new IllegalArgumentException("dir is null");
112: }
113: this .dir = dir;
114:
115: this .data = null;
116:
117: readData();
118: }
119:
120: /**
121: * Reads all the data for each app.
122: *
123: * @throws IOException if the content store failed
124: */
125: private void readData() throws IOException {
126: data = new HashMap();
127:
128: final JUMPNode.List dataDir = (JUMPNode.List) storeManager
129: .getNode(dir);
130: /*
131: * IMPL_NOTE: the presence of the corresponding dir must be ensured
132: * before
133: */
134: // assert dataDir != null : "application suite data dir is missing";
135: for (Iterator it = dataDir.getChildren(); it.hasNext();) {
136: final JUMPNode.Data elem = (JUMPNode.Data) it.next();
137: final int suiteId = convertNameToId(elem.getName());
138: final Object d = dataConverter.stringToData(elem.getData()
139: .getStringValue());
140:
141: final Integer key = new Integer(suiteId);
142: // assert !data.containsKey(key);
143: data.put(key, d);
144: }
145: }
146:
147: /** Callback for app suite data listing. */
148: static interface DataConsumer {
149: /**
150: * Consumes app suite data.
151: *
152: * @param suiteId app suite id
153: * @param data app suite data
154: */
155: void consume(int suiteId, Object data);
156: }
157:
158: /**
159: * Lists all the data.
160: *
161: * @param dataLister data lister
162: */
163: void listData(final DataConsumer dataLister) {
164: for (Iterator it = data.entrySet().iterator(); it.hasNext();) {
165: final Map.Entry entry = (Map.Entry) it.next();
166: final int suiteId = ((Integer) entry.getKey()).intValue();
167: final Object suiteData = entry.getValue();
168: dataLister.consume(suiteId, suiteData);
169: }
170: }
171:
172: /**
173: * Gets data associated with the app suite.
174: *
175: * @param suiteId app suite to fetch data for
176: *
177: * @return data or <code>null</code>
178: */
179: Object getSuiteData(final int suiteId) {
180: return data.get(new Integer(suiteId));
181: }
182:
183: /**
184: * Updates data for the given suite.
185: *
186: * @param suiteId app suite to update data for
187: * @param suiteData data to store
188: *
189: * @throws IOException if the content store failed
190: */
191: void updateSuiteData(final int suiteId, final Object suiteData)
192: throws IOException {
193: if (suiteData == null) {
194: throw new IllegalArgumentException("suiteData is null");
195: }
196:
197: final String nodeUri = makeURI(suiteId);
198: final Integer key = new Integer(suiteId);
199: final JUMPData jumpData = new JUMPData(dataConverter
200: .dataToString(suiteData));
201:
202: storeManager.safelyUpdateDataNode(nodeUri, jumpData);
203: data.put(key, suiteData);
204: }
205:
206: /**
207: * Removes data for the given suite.
208: *
209: * @param suiteId app suite to remove data for
210: *
211: * @throws IOException if the content store failed
212: */
213: void removeSuiteData(final int suiteId) throws IOException {
214: final String nodeUri = makeURI(suiteId);
215: storeManager.safelyDeleteDataNode(nodeUri);
216: data.remove(new Integer(suiteId));
217: }
218:
219: /** Radix to encode suite IDs. */
220: private static final int RADIX = 16;
221:
222: /**
223: * Forms an URI for the suite.
224: *
225: * @param suiteId app suite ID to form URI for
226: *
227: * @return URI
228: */
229: private String makeURI(final int suiteId) {
230: return dir + "/" + Integer.toString(suiteId, RADIX);
231: }
232:
233: /**
234: * Converts a name into app suite ID.
235: *
236: * @param name Name to convert
237: *
238: * @return app suite ID
239: */
240: private static int convertNameToId(final String name) {
241: return Integer.valueOf(name, RADIX).intValue();
242: }
243: }
|