001    /*
002      Copyright (C) 2003 Laurent Martelli <laurent@aopsys.com>
003      
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
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      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 program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
017      USA */
018    
019    package org.objectweb.jac.aspects.queue;
020    
021    
022    
023    import org.objectweb.jac.core.rtti.FieldItem;
024    import org.objectweb.jac.core.rtti.MethodItem;
025    import java.util.Collections;
026    import java.util.HashMap;
027    import java.util.HashSet;
028    import java.util.LinkedList;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.Set;
032    import org.objectweb.jac.util.Log;
033    
034    
035    /**
036     * Handles an asynchronous  message queue. 
037     */
038    public class MessageQueue {
039    
040       NotifyThread thread;
041       public MessageQueue() {
042          thread = new NotifyThread();
043          thread.start();
044       }
045    
046       // FieldItem -> Set of MethodItem
047       HashMap fieldClients = new HashMap();
048    
049       /**
050        * Gets the map of field change client callbacks.
051        * @return a Map whose keys are of type FieldItem, and whose values
052        * are sets of MethodItem
053        */
054       public Map getFieldClients() {
055          return fieldClients;
056       }
057    
058       List queue = Collections.synchronizedList(new LinkedList());
059    
060       /**
061        * Send a message to the queue saying that the field of an object
062        * has changed, so that it will be sent to all registered clients.
063        * @param substance the object whose field has changed
064        * @param field the field that changed
065        */
066       public void fieldChanged(Object substance, FieldItem field, 
067                                Object previousValue, Object currentValue) {
068          Log.trace("mqueue","fieldChanged("+substance+","+field+","+
069                    previousValue+" -> "+currentValue);
070          queue.add(new FieldChangeEvent(substance,field,previousValue,currentValue));
071          thread.notifyClients();
072       }
073    
074       /**
075        * Register for the changes of a field.
076        * @param field the field to register for
077        * @param callback a static method to be called when the field changes. 
078        * @see #unregisterFieldChange(FieldItem,MethodItem)
079        */
080       public void registerFieldChange(FieldItem field, MethodItem callback) {
081          Set clients = (Set)fieldClients.get(field);
082          if (clients==null) {
083             clients = new HashSet();
084             fieldClients.put(field,clients);
085          }
086          clients.add(callback);
087       }
088    
089       /**
090        * Notify registered clients of a field change.
091        * @param event the FieldChangeEvent to dispatch
092        */
093       public void notifyFieldChange(FieldChangeEvent event) {
094          Set clients = (Set)fieldClients.get(event.getField());
095          if (clients!=null) {
096             MethodItem[] array = (MethodItem[])clients.toArray(new MethodItem[] {});
097             Object[] params = new Object[] {event};
098             for(int i=0; i<array.length; i++) {
099                try {
100                   Log.warning("mqueue","notifying "+event+" to "+array[i]);
101                   array[i].invokeStatic(params);
102                } catch (Exception e) {
103                   Log.warning("mqueue","Failed to invoke "+array[i]+": "+e);
104                }
105             }
106          }
107       }
108    
109       /**
110        * Unregister for the changes of a field.
111        * @param field the field to unregister from
112        * @param callback a static method to to call anymore when the
113        * field changes.
114        * @see #registerFieldChange(FieldItem,MethodItem) */
115       public void unregisterFieldChange(FieldItem field, MethodItem callback) {
116          Set clients = (Set)fieldClients.get(field);
117          if (clients!=null) {
118             clients.remove(callback);
119          }
120       }
121    
122       class NotifyThread extends Thread {
123          public void run() {
124             while (true) {
125                try {
126                   synchronized(this) {
127                      this.wait();
128                   }
129                } catch (InterruptedException e) {
130                }
131                Log.trace("mqueue","Queue = "+queue);
132                if (!queue.isEmpty()) {
133                   Object events[] = queue.toArray();
134                   for (int i=0; i<events.length; i++) {
135                      if (events[i] instanceof FieldChangeEvent) {
136                         FieldChangeEvent event = (FieldChangeEvent)events[i];
137                         notifyFieldChange(event);
138                         queue.remove(event);
139                      }
140                   }
141                }
142             }
143          }
144    
145          /**
146           * Call this method when a new event was added to the queue.
147           */
148          public synchronized void notifyClients() {
149             this.notify();
150          }
151       }
152    }