KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > util > property > PropertyMap


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.util.property;
23
24 import java.io.IOException JavaDoc;
25 import java.io.ObjectInputStream JavaDoc;
26 import java.io.ObjectOutputStream JavaDoc;
27 import java.security.AccessController JavaDoc;
28 import java.security.PrivilegedAction JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.LinkedList JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.Set JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.Collections JavaDoc;
38 import java.util.Properties JavaDoc;
39 import javax.naming.Context JavaDoc;
40 import javax.naming.ldap.LdapContext JavaDoc;
41
42 import org.jboss.util.NullArgumentException;
43
44 /**
45  * A replacement for the standard <code>java.util.Properties</code>
46  * class which adds, among others, property event capabilities.
47  *
48  * @todo consider moving the JNDI property handling to a InitialContextFactoryBuilder
49  *
50  * @version <tt>$Revision: 1958 $</tt>
51  * @author <a HREF="mailto:jason@planet57.com">Jason Dillon</a>
52  * @author Scott.Stark@jboss.org
53  * @author <a HREF="mailto:adrian@jboss.com">Adrian Brock</a>
54  */

55 public class PropertyMap extends Properties JavaDoc
56 {
57    /** Serial version uid */
58    private static final long serialVersionUID = 8747802918099008518L;
59
60    /** Property name separator */
61    public static final String JavaDoc PROPERTY_NAME_SEPARATOR = ".";
62
63    /** Empty array property */
64    public static final String JavaDoc[] EMPTY_ARRAY_PROPERTY = new String JavaDoc[0];
65
66    /** Property listener list */
67    protected transient List JavaDoc unboundListeners;
68
69    /** Bound property name -> listener list map */
70    protected transient Map JavaDoc boundListeners;
71
72    /**
73     * This map avoids heavy contention for the properties that JNDI looks
74     * up everytime a new InitialContext instance is created. Once the container is
75     * up and running getProperty calls other than for the JNDI property are very rare,
76     * so the double lookup is not much of a performance problem.
77     * If at all possible, this class should be read-only and use no locks at all.
78     */

79    private transient Map JavaDoc jndiMap;
80    private final static Object JavaDoc NULL_VALUE = new Object JavaDoc();
81
82    /**
83     * Construct a PropertyMap with default properties.
84     *
85     * @param defaults Default properties.
86     */

87    public PropertyMap(Properties JavaDoc defaults)
88    {
89       super(defaults);
90       init();
91    }
92
93    /**
94     * Construct a PropertyMap.
95     */

96    public PropertyMap()
97    {
98       this(null);
99    }
100
101    /** Initialized listener lists and the JNDI properties cache map
102     */

103    private void init()
104    {
105       unboundListeners = Collections.synchronizedList(new ArrayList JavaDoc());
106       boundListeners = Collections.synchronizedMap(new HashMap JavaDoc());
107       jndiMap = new HashMap JavaDoc();
108
109       PrivilegedAction JavaDoc action = new PrivilegedAction JavaDoc()
110       {
111          public Object JavaDoc run()
112          {
113             Object JavaDoc value = System.getProperty(Context.PROVIDER_URL);
114             if (value == null) value = NULL_VALUE;
115             jndiMap.put(Context.PROVIDER_URL, value);
116
117             value = System.getProperty(Context.INITIAL_CONTEXT_FACTORY);
118             if (value == null) value = NULL_VALUE;
119             jndiMap.put(Context.INITIAL_CONTEXT_FACTORY, value);
120
121             value = System.getProperty(Context.OBJECT_FACTORIES);
122             if (value == null) value = NULL_VALUE;
123             jndiMap.put(Context.OBJECT_FACTORIES, value);
124
125             value = System.getProperty(Context.URL_PKG_PREFIXES);
126             if (value == null) value = NULL_VALUE;
127             jndiMap.put(Context.URL_PKG_PREFIXES, value);
128
129             value = System.getProperty(Context.STATE_FACTORIES);
130             if (value == null) value = NULL_VALUE;
131             jndiMap.put(Context.STATE_FACTORIES, value);
132
133             value = System.getProperty(Context.DNS_URL);
134             if (value == null) value = NULL_VALUE;
135             jndiMap.put(Context.DNS_URL, value);
136
137             value = System.getProperty(LdapContext.CONTROL_FACTORIES);
138             if (value == null) value = NULL_VALUE;
139             jndiMap.put(LdapContext.CONTROL_FACTORIES, value);
140             return null;
141          }
142       };
143       AccessController.doPrivileged(action);
144    }
145
146    /** Called by setProperty to update the jndiMap cache values.
147     * @param name the property name
148     * @param value the property value
149     */

150    private void updateJndiCache(String JavaDoc name, String JavaDoc value)
151    {
152       if( name == null )
153          return;
154
155       boolean isJndiProperty = name.equals(Context.PROVIDER_URL)
156          || name.equals(Context.INITIAL_CONTEXT_FACTORY)
157          || name.equals(Context.OBJECT_FACTORIES)
158          || name.equals(Context.URL_PKG_PREFIXES)
159          || name.equals(Context.STATE_FACTORIES)
160          || name.equals(Context.DNS_URL)
161          || name.equals(LdapContext.CONTROL_FACTORIES)
162       ;
163       if( isJndiProperty == true )
164          jndiMap.put(name, value);
165    }
166
167    /**
168     * Set a property.
169     *
170     * @param name Property name.
171     * @param value Property value.
172     * @return Previous property value or null.
173     */

174    public Object JavaDoc put(Object JavaDoc name, Object JavaDoc value)
175    {
176       if (name == null)
177          throw new NullArgumentException("name");
178       // value can be null
179

180       // check if this is a new addition or not prior to updating the hash
181
boolean add = !containsKey(name);
182       Object JavaDoc prev = super.put(name, value);
183
184       PropertyEvent event =
185          new PropertyEvent(this, name.toString(), value.toString());
186
187       // fire propertyAdded or propertyChanged
188
if (add)
189       {
190          firePropertyAdded(event);
191       }
192       else
193       {
194          firePropertyChanged(event);
195       }
196
197       return prev;
198    }
199
200    /**
201     * Remove a property.
202     *
203     * @param name Property name.
204     * @return Removed property value.
205     */

206    public Object JavaDoc remove(Object JavaDoc name)
207    {
208       if (name == null)
209          throw new NullArgumentException("name");
210
211       // check if there is a property with this name
212
boolean contains = containsKey(name);
213       Object JavaDoc value = null;
214
215       if (contains)
216       {
217          value = super.remove(name);
218          if (defaults != null)
219          {
220             Object JavaDoc obj = defaults.remove(name);
221             if (value == null)
222             {
223                value = obj;
224             }
225          }
226          // Remove any JNDI property value
227
jndiMap.remove(name);
228
229          PropertyEvent event = new PropertyEvent(this, name.toString(), value.toString());
230          firePropertyRemoved(event);
231       }
232
233       return value;
234    }
235
236    /**
237     * Returns a set of keys for all entries in this group and optionally
238     * all of the keys in the defaults map.
239     */

240    public Set JavaDoc keySet(final boolean includeDefaults)
241    {
242       if (includeDefaults)
243       {
244          Set JavaDoc set = new HashSet JavaDoc();
245          set.addAll(defaults.keySet());
246          set.addAll(super.keySet());
247          return Collections.synchronizedSet(set);
248       }
249
250       return super.keySet();
251    }
252
253    /**
254     * Returns a set of entrys for all entries in this group and optionally
255     * all of the entrys in the defaults map.
256     */

257    public Set JavaDoc entrySet(final boolean includeDefaults)
258    {
259       if (includeDefaults)
260       {
261          Set JavaDoc set = new HashSet JavaDoc();
262          set.addAll(defaults.entrySet());
263          set.addAll(super.entrySet());
264          return Collections.synchronizedSet(set);
265       }
266
267       return super.entrySet();
268    }
269
270    /**
271     * Add a property listener.
272     *
273     * @param listener Property listener to add.
274     */

275    public void addPropertyListener(PropertyListener listener)
276    {
277       if (listener == null)
278          throw new NullArgumentException("listener");
279
280       if (listener instanceof BoundPropertyListener)
281       {
282          addPropertyListener((BoundPropertyListener) listener);
283       }
284       else
285       {
286          // only add the listener if it is not in the list already
287
if (!unboundListeners.contains(listener))
288             unboundListeners.add(listener);
289       }
290    }
291
292    /**
293     * Add a bound property listener.
294     *
295     * @param listener Bound property listener to add.
296     */

297    protected void addPropertyListener(BoundPropertyListener listener)
298    {
299       // get the bound property name
300
String JavaDoc name = listener.getPropertyName();
301
302       // get the bound listener list for the property
303
List JavaDoc list = (List JavaDoc) boundListeners.get(name);
304       
305       // if list is null, then add a new list
306
if (list == null)
307       {
308          list = Collections.synchronizedList(new ArrayList JavaDoc());
309          boundListeners.put(name, list);
310       }
311       
312       // if listener is not in the list already, then add it
313
if (!list.contains(listener))
314       {
315          list.add(listener);
316          // notify listener that is is bound
317
listener.propertyBound(this);
318       }
319    }
320
321    /**
322     * Add an array of property listeners.
323     *
324     * @param listeners Array of property listeners to add.
325     */

326    public void addPropertyListeners(PropertyListener[] listeners)
327    {
328       if (listeners == null)
329          throw new NullArgumentException("listeners");
330
331       for (int i = 0; i < listeners.length; i++)
332       {
333          addPropertyListener(listeners[i]);
334       }
335    }
336
337    /**
338     * Remove a property listener.
339     *
340     * @param listener Property listener to remove.
341     * @return True if listener was removed.
342     */

343    public boolean removePropertyListener(PropertyListener listener)
344    {
345       if (listener == null)
346          throw new NullArgumentException("listener");
347
348       boolean removed = false;
349       if (listener instanceof BoundPropertyListener)
350       {
351          removed = removePropertyListener((BoundPropertyListener) listener);
352       }
353       else
354       {
355          removed = unboundListeners.remove(listener);
356       }
357
358       return removed;
359    }
360
361    /**
362     * Remove a bound property listener.
363     *
364     * @param listener Bound property listener to remove.
365     * @return True if listener was removed.
366     */

367    protected boolean removePropertyListener(BoundPropertyListener listener)
368    {
369       // get the bound property name
370
String JavaDoc name = listener.getPropertyName();
371       
372       // get the bound listener list for the property
373
List JavaDoc list = (List JavaDoc) boundListeners.get(name);
374       boolean removed = false;
375       if (list != null)
376       {
377          removed = list.remove(listener);
378          
379          // notify listener that is was unbound
380
if (removed) listener.propertyUnbound(this);
381       }
382       return removed;
383    }
384
385    /**
386     * Fire a property added event to the given list of listeners.
387     *
388     * @param list Listener list.
389     * @param event Property event.
390     */

391    private void firePropertyAdded(List JavaDoc list, PropertyEvent event)
392    {
393       if (list == null) return;
394
395       int size = list.size();
396       for (int i = 0; i < size; i++)
397       {
398          PropertyListener listener = (PropertyListener) list.get(i);
399          listener.propertyAdded(event);
400       }
401    }
402
403    /**
404     * Fire a property added event to all registered listeners.
405     *
406     * @param event Property event.
407     */

408    protected void firePropertyAdded(PropertyEvent event)
409    {
410       // fire all bound listeners (if any) first
411
if (boundListeners != null)
412       {
413          List JavaDoc list = (List JavaDoc) boundListeners.get(event.getPropertyName());
414          if (list != null)
415          {
416             firePropertyAdded(list, event);
417          }
418       }
419
420       // next fire all unbound listeners
421
firePropertyAdded(unboundListeners, event);
422    }
423
424    /**
425     * Fire a property removed event to the given list of listeners.
426     *
427     * @param list Listener list.
428     * @param event Property event.
429     */

430    private void firePropertyRemoved(List JavaDoc list, PropertyEvent event)
431    {
432       if (list == null) return;
433
434       int size = list.size();
435       for (int i = 0; i < size; i++)
436       {
437          PropertyListener listener = (PropertyListener) list.get(i);
438          listener.propertyRemoved(event);
439       }
440    }
441
442    /**
443     * Fire a property removed event to all registered listeners.
444     *
445     * @param event Property event.
446     */

447    protected void firePropertyRemoved(PropertyEvent event)
448    {
449       // fire all bound listeners (if any) first
450
if (boundListeners != null)
451       {
452          List JavaDoc list = (List JavaDoc) boundListeners.get(event.getPropertyName());
453          if (list != null)
454          {
455             firePropertyRemoved(list, event);
456          }
457       }
458
459       // next fire all unbound listeners
460
firePropertyRemoved(unboundListeners, event);
461    }
462
463    /**
464     * Fire a property changed event to the given list of listeners.
465     *
466     * @param list Listener list.
467     * @param event Property event.
468     */

469    private void firePropertyChanged(List JavaDoc list, PropertyEvent event)
470    {
471       if (list == null) return;
472
473       int size = list.size();
474       for (int i = 0; i < size; i++)
475       {
476          PropertyListener listener = (PropertyListener) list.get(i);
477          listener.propertyChanged(event);
478       }
479    }
480
481    /**
482     * Fire a property changed event to all listeners.
483     *
484     * @param event Property event.
485     */

486    protected void firePropertyChanged(PropertyEvent event)
487    {
488       // fire all bound listeners (if any) first
489
if (boundListeners != null)
490       {
491          List JavaDoc list = (List JavaDoc) boundListeners.get(event.getPropertyName());
492          if (list != null)
493          {
494             firePropertyChanged(list, event);
495          }
496       }
497
498       // next fire all unbound listeners
499
firePropertyChanged(unboundListeners, event);
500    }
501
502    /**
503     * Make a optionaly prefixed property name.
504     *
505     * @param base Base property name.
506     * @param prefix Optional prefix (can be null).
507     * @return Property name.
508     */

509    protected String JavaDoc makePrefixedPropertyName(String JavaDoc base, String JavaDoc prefix)
510    {
511       String JavaDoc name = base;
512
513       if (prefix != null)
514       {
515          StringBuffer JavaDoc buff = new StringBuffer JavaDoc(base);
516          if (prefix != null)
517          {
518             buff.insert(0, PROPERTY_NAME_SEPARATOR);
519             buff.insert(0, prefix);
520          }
521          return buff.toString();
522       }
523
524       return name;
525    }
526
527    /**
528     * Load properties from a map.
529     *
530     * @param prefix Prefix to append to all map keys (or null).
531     * @param map Map containing properties to load.
532     */

533    public void load(String JavaDoc prefix, Map JavaDoc map) throws PropertyException
534    {
535       // prefix can be null
536
if (map == null)
537          throw new NullArgumentException("map");
538
539       // set properties for each key in map
540
Iterator JavaDoc iter = map.keySet().iterator();
541       while (iter.hasNext())
542       {
543          // make a string key with optional prefix
544
String JavaDoc key = String.valueOf(iter.next());
545          String JavaDoc name = makePrefixedPropertyName(key, prefix);
546          String JavaDoc value = String.valueOf(map.get(name));
547
548          // set the property
549
setProperty(name, value);
550       }
551    }
552
553    /**
554     * Load properties from a map.
555     *
556     * @param map Map containing properties to load.
557     */

558    public void load(Map JavaDoc map) throws PropertyException
559    {
560       load(null, map);
561    }
562
563    /**
564     * Load properties from a PropertyReader.
565     *
566     * @param reader PropertyReader to read properties from.
567     */

568    public void load(PropertyReader reader) throws PropertyException, IOException JavaDoc
569    {
570       if (reader == null)
571          throw new NullArgumentException("reader");
572
573       load(reader.readProperties());
574    }
575
576    /**
577     * Load properties from a PropertyReader specifed by the given class name.
578     *
579     * @param className Class name of a PropertyReader to read from.
580     */

581    public void load(String JavaDoc className) throws PropertyException, IOException JavaDoc
582    {
583       if (className == null)
584          throw new NullArgumentException("className");
585
586       PropertyReader reader = null;
587
588       try
589       {
590          Class JavaDoc type = Class.forName(className);
591          reader = (PropertyReader) type.newInstance();
592       }
593       catch (Exception JavaDoc e)
594       {
595          throw new PropertyException(e);
596       }
597          
598       // load the properties from the source
599
load(reader);
600    }
601
602    /**
603     * Set a property.
604     *
605     * <p>Returns Object instead of String due to limitations with
606     * <code>java.util.Properties</code>.
607     *
608     * @param name Property name.
609     * @param value Property value.
610     * @return Previous property value or null.
611     */

612    public Object JavaDoc setProperty(String JavaDoc name, String JavaDoc value)
613    {
614       updateJndiCache(name, value);
615       return put(name, value);
616    }
617
618    public String JavaDoc getProperty(String JavaDoc name)
619    {
620       Object JavaDoc value = jndiMap.get(name);
621       if (value != null)
622       {
623          // key was in the map
624
return (value == NULL_VALUE) ? null : (String JavaDoc) value;
625       }
626       return super.getProperty(name);
627    }
628
629    /**
630     * Remove a property.
631     *
632     * @param name Property name.
633     * @return Removed property value or null.
634     */

635    public String JavaDoc removeProperty(String JavaDoc name)
636    {
637       return (String JavaDoc) remove(name);
638    }
639
640
641    /**
642     * Make an indexed property name.
643     *
644     * @param base Base property name.
645     * @param index Property index.
646     * @return Indexed property name.
647     */

648    protected String JavaDoc makeIndexPropertyName(String JavaDoc base, int index)
649    {
650       return base + PROPERTY_NAME_SEPARATOR + index;
651    }
652
653    /**
654     * Get an array style property.
655     *
656     * <p>Array properties are specified as:
657     * <code>base_property_name.<b>INDEX</b>.
658     *
659     * <p>Indexes begin with zero and must be contiguous. A break in
660     * continuity signals the end of the array.
661     *
662     * @param base Base property name.
663     * @param defaultValues Default property values.
664     * @return Array of property values or default.
665     */

666    public String JavaDoc[] getArrayProperty(String JavaDoc base, String JavaDoc[] defaultValues)
667    {
668       if (base == null)
669          throw new NullArgumentException("base");
670
671       // create a new list to store indexed values into
672
List JavaDoc list = new LinkedList JavaDoc();
673
674       int i = 0;
675       while (true)
676       {
677          // make the index property name
678
String JavaDoc name = makeIndexPropertyName(base, i);
679
680          // see if there is a value for this property
681
String JavaDoc value = getProperty(name);
682
683          if (value != null)
684          {
685             list.add(value);
686          }
687          else if (i >= 0)
688          {
689             break; // no more index properties
690
}
691
692          i++;
693       }
694
695       String JavaDoc values[] = defaultValues;
696
697       // if the list is not empty, then return it as an array
698
if (list.size() != 0)
699       {
700          values = (String JavaDoc[]) list.toArray(new String JavaDoc[list.size()]);
701       }
702
703       return values;
704    }
705
706    /**
707     * Get an array style property.
708     *
709     * @param name Property name.
710     * @return Array of property values or empty array.
711     */

712    public String JavaDoc[] getArrayProperty(String JavaDoc name)
713    {
714       return getArrayProperty(name, EMPTY_ARRAY_PROPERTY);
715    }
716
717    /**
718     * Return an iterator over all contained property names.
719     *
720     * @return Property name iterator.
721     */

722    public Iterator JavaDoc names()
723    {
724       return keySet().iterator();
725    }
726
727    /**
728     * Check if this map contains a given property.
729     *
730     * @param name Property name.
731     * @return True if contains property.
732     */

733    public boolean containsProperty(String JavaDoc name)
734    {
735       return containsKey(name);
736    }
737
738    /**
739     * Get a property group for the given property base.
740     *
741     * @param basename Base property name.
742     * @return Property group.
743     */

744    public PropertyGroup getPropertyGroup(String JavaDoc basename)
745    {
746       return new PropertyGroup(basename, this);
747    }
748
749    /**
750     * Get a property group for the given property base at the given index.
751     *
752     * @param basename Base property name.
753     * @param index Array property index.
754     * @return Property group.
755     */

756    public PropertyGroup getPropertyGroup(String JavaDoc basename, int index)
757    {
758       String JavaDoc name = makeIndexPropertyName(basename, index);
759       return getPropertyGroup(name);
760    }
761
762    private void readObject(ObjectInputStream JavaDoc stream)
763       throws IOException JavaDoc, ClassNotFoundException JavaDoc
764    {
765       // reset the listener lists
766
init();
767
768       stream.defaultReadObject();
769    }
770
771    private void writeObject(ObjectOutputStream JavaDoc stream)
772       throws IOException JavaDoc
773    {
774       stream.defaultWriteObject();
775    }
776 }
777
Popular Tags