001: /**
002: * $RCSfile$
003: * $Revision: 9307 $
004: * $Date: 2007-10-12 15:35:17 -0700 (Fri, 12 Oct 2007) $
005: *
006: * Copyright (C) 1999-2004 Jive Software. All rights reserved.
007: *
008: * This software is the proprietary information of Jive Software.
009: * Use is subject to license terms.
010: */package org.jivesoftware.util;
011:
012: import org.jivesoftware.database.DbConnectionManager;
013: import org.jivesoftware.util.cache.CacheFactory;
014:
015: import java.sql.Connection;
016: import java.sql.PreparedStatement;
017: import java.sql.ResultSet;
018: import java.sql.SQLException;
019: import java.util.*;
020: import java.util.concurrent.ConcurrentHashMap;
021:
022: /**
023: * Retrieves and stores Jive properties. Properties are stored in the database.
024: *
025: * @author Matt Tucker
026: */
027: public class JiveProperties implements Map<String, String> {
028:
029: private static final String LOAD_PROPERTIES = "SELECT name, propValue FROM jiveProperty";
030: private static final String INSERT_PROPERTY = "INSERT INTO jiveProperty(name, propValue) VALUES(?,?)";
031: private static final String UPDATE_PROPERTY = "UPDATE jiveProperty SET propValue=? WHERE name=?";
032: private static final String DELETE_PROPERTY = "DELETE FROM jiveProperty WHERE name LIKE ?";
033:
034: private static class JivePropertyHolder {
035: private static final JiveProperties instance = new JiveProperties();
036: static {
037: instance.init();
038: }
039: }
040:
041: private Map<String, String> properties;
042:
043: /**
044: * Returns a singleton instance of JiveProperties.
045: *
046: * @return an instance of JiveProperties.
047: */
048: public static JiveProperties getInstance() {
049: return JivePropertyHolder.instance;
050: }
051:
052: private JiveProperties() {
053: }
054:
055: /**
056: * For internal use only. This method allows for the reloading of all properties from the
057: * values in the datatabase. This is required since it's quite possible during the setup
058: * process that a database connection will not be available till after this class is
059: * initialized. Thus, if there are existing properties in the database we will want to reload
060: * this class after the setup process has been completed.
061: */
062: public void init() {
063: if (properties == null) {
064: properties = new ConcurrentHashMap<String, String>();
065: } else {
066: properties.clear();
067: }
068:
069: loadProperties();
070: }
071:
072: public int size() {
073: return properties.size();
074: }
075:
076: public void clear() {
077: throw new UnsupportedOperationException();
078: }
079:
080: public boolean isEmpty() {
081: return properties.isEmpty();
082: }
083:
084: public boolean containsKey(Object key) {
085: return properties.containsKey(key);
086: }
087:
088: public boolean containsValue(Object value) {
089: return properties.containsValue(value);
090: }
091:
092: public Collection<String> values() {
093: return Collections.unmodifiableCollection(properties.values());
094: }
095:
096: public void putAll(Map<? extends String, ? extends String> t) {
097: for (Map.Entry<? extends String, ? extends String> entry : t
098: .entrySet()) {
099: put(entry.getKey(), entry.getValue());
100: }
101: }
102:
103: public Set<Map.Entry<String, String>> entrySet() {
104: return Collections.unmodifiableSet(properties.entrySet());
105: }
106:
107: public Set<String> keySet() {
108: return Collections.unmodifiableSet(properties.keySet());
109: }
110:
111: public String get(Object key) {
112: return properties.get(key);
113: }
114:
115: /**
116: * Return all children property names of a parent property as a Collection
117: * of String objects. For example, given the properties <tt>X.Y.A</tt>,
118: * <tt>X.Y.B</tt>, and <tt>X.Y.C</tt>, then the child properties of
119: * <tt>X.Y</tt> are <tt>X.Y.A</tt>, <tt>X.Y.B</tt>, and <tt>X.Y.C</tt>. The method
120: * is not recursive; ie, it does not return children of children.
121: *
122: * @param parentKey the name of the parent property.
123: * @return all child property names for the given parent.
124: */
125: public Collection<String> getChildrenNames(String parentKey) {
126: Collection<String> results = new HashSet<String>();
127: for (String key : properties.keySet()) {
128: if (key.startsWith(parentKey + ".")) {
129: if (key.equals(parentKey)) {
130: continue;
131: }
132: int dotIndex = key.indexOf(".", parentKey.length() + 1);
133: if (dotIndex < 1) {
134: if (!results.contains(key)) {
135: results.add(key);
136: }
137: } else {
138: String name = parentKey
139: + key.substring(parentKey.length(),
140: dotIndex);
141: results.add(name);
142: }
143: }
144: }
145: return results;
146: }
147:
148: /**
149: * Returns all property names as a Collection of String values.
150: *
151: * @return all property names.
152: */
153: public Collection<String> getPropertyNames() {
154: return properties.keySet();
155: }
156:
157: public String remove(Object key) {
158: String value;
159: synchronized (this ) {
160: value = properties.remove(key);
161: // Also remove any children.
162: Collection<String> propNames = getPropertyNames();
163: for (String name : propNames) {
164: if (name.startsWith((String) key)) {
165: properties.remove(name);
166: }
167: }
168: deleteProperty((String) key);
169: }
170:
171: // Generate event.
172: Map<String, Object> params = Collections.emptyMap();
173: PropertyEventDispatcher.dispatchEvent((String) key,
174: PropertyEventDispatcher.EventType.property_deleted,
175: params);
176:
177: // Send update to other cluster members.
178: CacheFactory.doClusterTask(PropertyClusterEventTask
179: .createDeteleTask((String) key));
180:
181: return value;
182: }
183:
184: void localRemove(String key) {
185: properties.remove(key);
186: // Also remove any children.
187: Collection<String> propNames = getPropertyNames();
188: for (String name : propNames) {
189: if (name.startsWith(key)) {
190: properties.remove(name);
191: }
192: }
193:
194: // Generate event.
195: Map<String, Object> params = Collections.emptyMap();
196: PropertyEventDispatcher.dispatchEvent(key,
197: PropertyEventDispatcher.EventType.property_deleted,
198: params);
199: }
200:
201: public String put(String key, String value) {
202: if (key == null || value == null) {
203: throw new NullPointerException(
204: "Key or value cannot be null. Key=" + key
205: + ", value=" + value);
206: }
207: if (key.endsWith(".")) {
208: key = key.substring(0, key.length() - 1);
209: }
210: key = key.trim();
211: String result;
212: synchronized (this ) {
213: if (properties.containsKey(key)) {
214: if (!properties.get(key).equals(value)) {
215: updateProperty(key, value);
216: }
217: } else {
218: insertProperty(key, value);
219: }
220:
221: result = properties.put(key, value);
222: }
223:
224: // Generate event.
225: Map<String, Object> params = new HashMap<String, Object>();
226: params.put("value", value);
227: PropertyEventDispatcher.dispatchEvent(key,
228: PropertyEventDispatcher.EventType.property_set, params);
229:
230: // Send update to other cluster members.
231: CacheFactory.doClusterTask(PropertyClusterEventTask
232: .createPutTask(key, value));
233:
234: return result;
235: }
236:
237: void localPut(String key, String value) {
238: properties.put(key, value);
239:
240: // Generate event.
241: Map<String, Object> params = new HashMap<String, Object>();
242: params.put("value", value);
243: PropertyEventDispatcher.dispatchEvent(key,
244: PropertyEventDispatcher.EventType.property_set, params);
245: }
246:
247: public String getProperty(String name, String defaultValue) {
248: String value = properties.get(name);
249: if (value != null) {
250: return value;
251: } else {
252: return defaultValue;
253: }
254: }
255:
256: public boolean getBooleanProperty(String name) {
257: return Boolean.valueOf(get(name));
258: }
259:
260: public boolean getBooleanProperty(String name, boolean defaultValue) {
261: String value = get(name);
262: if (value != null) {
263: return Boolean.valueOf(value);
264: } else {
265: return defaultValue;
266: }
267: }
268:
269: private void insertProperty(String name, String value) {
270: Connection con = null;
271: PreparedStatement pstmt = null;
272: try {
273: con = DbConnectionManager.getConnection();
274: pstmt = con.prepareStatement(INSERT_PROPERTY);
275: pstmt.setString(1, name);
276: pstmt.setString(2, value);
277: pstmt.executeUpdate();
278: } catch (SQLException e) {
279: Log.error(e);
280: } finally {
281: try {
282: if (pstmt != null) {
283: pstmt.close();
284: }
285: } catch (Exception e) {
286: Log.error(e);
287: }
288: try {
289: if (con != null) {
290: con.close();
291: }
292: } catch (Exception e) {
293: Log.error(e);
294: }
295: }
296: }
297:
298: private void updateProperty(String name, String value) {
299: Connection con = null;
300: PreparedStatement pstmt = null;
301: try {
302: con = DbConnectionManager.getConnection();
303: pstmt = con.prepareStatement(UPDATE_PROPERTY);
304: pstmt.setString(1, value);
305: pstmt.setString(2, name);
306: pstmt.executeUpdate();
307: } catch (SQLException e) {
308: Log.error(e);
309: } finally {
310: try {
311: if (pstmt != null) {
312: pstmt.close();
313: }
314: } catch (Exception e) {
315: Log.error(e);
316: }
317: try {
318: if (con != null) {
319: con.close();
320: }
321: } catch (Exception e) {
322: Log.error(e);
323: }
324: }
325: }
326:
327: private void deleteProperty(String name) {
328: Connection con = null;
329: PreparedStatement pstmt = null;
330: try {
331: con = DbConnectionManager.getConnection();
332: pstmt = con.prepareStatement(DELETE_PROPERTY);
333: pstmt.setString(1, name + "%");
334: pstmt.executeUpdate();
335: } catch (SQLException e) {
336: Log.error(e);
337: } finally {
338: try {
339: if (pstmt != null) {
340: pstmt.close();
341: }
342: } catch (Exception e) {
343: Log.error(e);
344: }
345: try {
346: if (con != null) {
347: con.close();
348: }
349: } catch (Exception e) {
350: Log.error(e);
351: }
352: }
353: }
354:
355: private void loadProperties() {
356: Connection con = null;
357: PreparedStatement pstmt = null;
358: try {
359: con = DbConnectionManager.getConnection();
360: pstmt = con.prepareStatement(LOAD_PROPERTIES);
361: ResultSet rs = pstmt.executeQuery();
362: while (rs.next()) {
363: String name = rs.getString(1);
364: String value = rs.getString(2);
365: properties.put(name, value);
366: }
367: rs.close();
368: } catch (Exception e) {
369: Log.error(e);
370: } finally {
371: try {
372: if (pstmt != null) {
373: pstmt.close();
374: }
375: } catch (Exception e) {
376: Log.error(e);
377: }
378: try {
379: if (con != null) {
380: con.close();
381: }
382: } catch (Exception e) {
383: Log.error(e);
384: }
385: }
386: }
387: }
|