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 }