View Javadoc

1   /*
2    * Copyright  2004-2005 Stefan Reuter
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *
16   */
17  package net.sf.asterisk.manager.impl;
18  
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.Method;
21  import java.lang.reflect.Modifier;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.Map;
25  
26  import net.sf.asterisk.manager.EventBuilder;
27  import net.sf.asterisk.manager.event.AgentCallbackLoginEvent;
28  import net.sf.asterisk.manager.event.AgentCallbackLogoffEvent;
29  import net.sf.asterisk.manager.event.AgentCalledEvent;
30  import net.sf.asterisk.manager.event.AgentCompleteEvent;
31  import net.sf.asterisk.manager.event.AgentConnectEvent;
32  import net.sf.asterisk.manager.event.AgentDumpEvent;
33  import net.sf.asterisk.manager.event.AgentLoginEvent;
34  import net.sf.asterisk.manager.event.AgentLogoffEvent;
35  import net.sf.asterisk.manager.event.AgentsCompleteEvent;
36  import net.sf.asterisk.manager.event.AgentsEvent;
37  import net.sf.asterisk.manager.event.AlarmClearEvent;
38  import net.sf.asterisk.manager.event.AlarmEvent;
39  import net.sf.asterisk.manager.event.CdrEvent;
40  import net.sf.asterisk.manager.event.DBGetResponseEvent;
41  import net.sf.asterisk.manager.event.DNDStateEvent;
42  import net.sf.asterisk.manager.event.DialEvent;
43  import net.sf.asterisk.manager.event.ExtensionStatusEvent;
44  import net.sf.asterisk.manager.event.FaxReceivedEvent;
45  import net.sf.asterisk.manager.event.HangupEvent;
46  import net.sf.asterisk.manager.event.HoldEvent;
47  import net.sf.asterisk.manager.event.HoldedCallEvent;
48  import net.sf.asterisk.manager.event.JoinEvent;
49  import net.sf.asterisk.manager.event.LeaveEvent;
50  import net.sf.asterisk.manager.event.LinkEvent;
51  import net.sf.asterisk.manager.event.LogChannelEvent;
52  import net.sf.asterisk.manager.event.ManagerEvent;
53  import net.sf.asterisk.manager.event.MeetMeJoinEvent;
54  import net.sf.asterisk.manager.event.MeetMeLeaveEvent;
55  import net.sf.asterisk.manager.event.MessageWaitingEvent;
56  import net.sf.asterisk.manager.event.NewCallerIdEvent;
57  import net.sf.asterisk.manager.event.NewChannelEvent;
58  import net.sf.asterisk.manager.event.NewExtenEvent;
59  import net.sf.asterisk.manager.event.NewStateEvent;
60  import net.sf.asterisk.manager.event.OriginateFailureEvent;
61  import net.sf.asterisk.manager.event.OriginateSuccessEvent;
62  import net.sf.asterisk.manager.event.ParkedCallEvent;
63  import net.sf.asterisk.manager.event.ParkedCallGiveUpEvent;
64  import net.sf.asterisk.manager.event.ParkedCallTimeOutEvent;
65  import net.sf.asterisk.manager.event.ParkedCallsCompleteEvent;
66  import net.sf.asterisk.manager.event.PeerEntryEvent;
67  import net.sf.asterisk.manager.event.PeerStatusEvent;
68  import net.sf.asterisk.manager.event.PeerlistCompleteEvent;
69  import net.sf.asterisk.manager.event.QueueEntryEvent;
70  import net.sf.asterisk.manager.event.QueueMemberAddedEvent;
71  import net.sf.asterisk.manager.event.QueueMemberEvent;
72  import net.sf.asterisk.manager.event.QueueMemberPausedEvent;
73  import net.sf.asterisk.manager.event.QueueMemberRemovedEvent;
74  import net.sf.asterisk.manager.event.QueueMemberStatusEvent;
75  import net.sf.asterisk.manager.event.QueueParamsEvent;
76  import net.sf.asterisk.manager.event.QueueStatusCompleteEvent;
77  import net.sf.asterisk.manager.event.RegistryEvent;
78  import net.sf.asterisk.manager.event.ReloadEvent;
79  import net.sf.asterisk.manager.event.RenameEvent;
80  import net.sf.asterisk.manager.event.ResponseEvent;
81  import net.sf.asterisk.manager.event.ShutdownEvent;
82  import net.sf.asterisk.manager.event.StatusCompleteEvent;
83  import net.sf.asterisk.manager.event.StatusEvent;
84  import net.sf.asterisk.manager.event.UnholdEvent;
85  import net.sf.asterisk.manager.event.UnlinkEvent;
86  import net.sf.asterisk.manager.event.UnparkedCallEvent;
87  import net.sf.asterisk.manager.event.UserEvent;
88  import net.sf.asterisk.manager.event.ZapShowChannelsCompleteEvent;
89  import net.sf.asterisk.manager.event.ZapShowChannelsEvent;
90  import net.sf.asterisk.util.AstUtil;
91  import net.sf.asterisk.util.Log;
92  import net.sf.asterisk.util.LogFactory;
93  
94  /***
95   * Default implementation of the EventBuilder interface.
96   * 
97   * @see net.sf.asterisk.manager.event.ManagerEvent
98   * @author srt
99   * @version $Id: EventBuilderImpl.java,v 1.18 2005/10/26 06:39:30 srt Exp $
100  */
101 public class EventBuilderImpl implements EventBuilder
102 {
103     private final Log logger = LogFactory.getLog(getClass());
104     private Map registeredEventClasses;
105 
106     public EventBuilderImpl()
107     {
108         this.registeredEventClasses = new HashMap();
109         registerBuiltinEventClasses();
110     }
111 
112     private void registerBuiltinEventClasses()
113     {
114         registerEventClass(AgentCallbackLoginEvent.class);
115         registerEventClass(AgentCallbackLogoffEvent.class);
116         registerEventClass(AgentCalledEvent.class);
117         registerEventClass(AgentConnectEvent.class);
118         registerEventClass(AgentCompleteEvent.class);
119         registerEventClass(AgentDumpEvent.class);
120         registerEventClass(AgentLoginEvent.class);
121         registerEventClass(AgentLogoffEvent.class);
122         registerEventClass(AgentsEvent.class);
123         registerEventClass(AgentsCompleteEvent.class);
124         registerEventClass(AlarmEvent.class);
125         registerEventClass(AlarmClearEvent.class);
126         registerEventClass(CdrEvent.class);
127         registerEventClass(DBGetResponseEvent.class);
128         registerEventClass(DialEvent.class);
129         registerEventClass(DNDStateEvent.class);
130         registerEventClass(ExtensionStatusEvent.class);
131         registerEventClass(FaxReceivedEvent.class);
132         registerEventClass(HangupEvent.class);
133         registerEventClass(HoldedCallEvent.class);
134         registerEventClass(HoldEvent.class);
135         registerEventClass(JoinEvent.class);
136         registerEventClass(LeaveEvent.class);
137         registerEventClass(LinkEvent.class);
138         registerEventClass(LogChannelEvent.class);
139         registerEventClass(MeetMeJoinEvent.class);
140         registerEventClass(MeetMeLeaveEvent.class);
141         registerEventClass(MessageWaitingEvent.class);
142         registerEventClass(NewCallerIdEvent.class);
143         registerEventClass(NewChannelEvent.class);
144         registerEventClass(NewExtenEvent.class);
145         registerEventClass(NewStateEvent.class);
146         registerEventClass(OriginateFailureEvent.class);
147         registerEventClass(OriginateSuccessEvent.class);
148         registerEventClass(ParkedCallGiveUpEvent.class);
149         registerEventClass(ParkedCallEvent.class);
150         registerEventClass(ParkedCallTimeOutEvent.class);
151         registerEventClass(ParkedCallsCompleteEvent.class);
152         registerEventClass(PeerEntryEvent.class);
153         registerEventClass(PeerlistCompleteEvent.class);
154         registerEventClass(PeerStatusEvent.class);
155         registerEventClass(QueueEntryEvent.class);
156         registerEventClass(QueueMemberAddedEvent.class);
157         registerEventClass(QueueMemberEvent.class);
158         registerEventClass(QueueMemberPausedEvent.class);
159         registerEventClass(QueueMemberRemovedEvent.class);
160         registerEventClass(QueueMemberStatusEvent.class);
161         registerEventClass(QueueParamsEvent.class);
162         registerEventClass(QueueStatusCompleteEvent.class);
163         registerEventClass(RegistryEvent.class);
164         registerEventClass(ReloadEvent.class);
165         registerEventClass(RenameEvent.class);
166         registerEventClass(ShutdownEvent.class);
167         registerEventClass(StatusEvent.class);
168         registerEventClass(StatusCompleteEvent.class);
169         registerEventClass(UnholdEvent.class);
170         registerEventClass(UnlinkEvent.class);
171         registerEventClass(UnparkedCallEvent.class);
172         registerEventClass(ZapShowChannelsEvent.class);
173         registerEventClass(ZapShowChannelsCompleteEvent.class);
174     }
175 
176     public void registerEventClass(Class clazz)
177     {
178         String className;
179         String eventType;
180 
181         className = clazz.getName();
182         eventType = className.substring(className.lastIndexOf('.') + 1)
183                 .toLowerCase();
184 
185         if (eventType.endsWith("event"))
186         {
187             eventType = eventType.substring(0, eventType.length()
188                     - "event".length());
189         }
190 
191         if (UserEvent.class.isAssignableFrom(clazz)
192                 && !eventType.startsWith("userevent"))
193         {
194             eventType = "userevent" + eventType;
195         }
196 
197         registerEventClass(eventType, clazz);
198     }
199 
200     /***
201      * Registers a new event class for the event given by eventType.
202      * 
203      * @param eventType the name of the event to register the class for. For
204      *            example "Join".
205      * @param clazz the event class to register, must extend
206      *            net.sf.asterisk.manager.event.Event.
207      */
208     public void registerEventClass(String eventType, Class clazz)
209     {
210         Constructor defaultConstructor;
211 
212         if (!ManagerEvent.class.isAssignableFrom(clazz))
213         {
214             throw new IllegalArgumentException(clazz + " is not a ManagerEvent");
215         }
216 
217         if ((clazz.getModifiers() & Modifier.ABSTRACT) != 0)
218         {
219             throw new IllegalArgumentException(clazz + " is abstract");
220         }
221 
222         try
223         {
224             defaultConstructor = clazz
225                     .getConstructor(new Class[]{Object.class});
226         }
227         catch (NoSuchMethodException ex)
228         {
229             throw new IllegalArgumentException(clazz
230                     + " has no usable constructor");
231         }
232 
233         if ((defaultConstructor.getModifiers() & Modifier.PUBLIC) == 0)
234         {
235             throw new IllegalArgumentException(clazz
236                     + " has no public default constructor");
237         }
238 
239         registeredEventClasses.put(eventType.toLowerCase(), clazz);
240 
241         logger.debug("Registered event type '" + eventType + "' (" + clazz
242                 + ")");
243     }
244 
245     public ManagerEvent buildEvent(Object source, Map attributes)
246     {
247         ManagerEvent event;
248         String eventType;
249         Class eventClass;
250         Constructor constructor;
251 
252         if (attributes.get("event") == null)
253         {
254             logger.error("No event event type in properties");
255             return null;
256         }
257 
258         eventType = ((String) attributes.get("event")).toLowerCase();
259         eventClass = (Class) registeredEventClasses.get(eventType);
260         if (eventClass == null)
261         {
262             logger.info("No event class registered for event type '"
263                     + eventType + "', attributes: " + attributes);
264             return null;
265         }
266 
267         try
268         {
269             constructor = eventClass.getConstructor(new Class[]{Object.class});
270         }
271         catch (NoSuchMethodException ex)
272         {
273             logger.error("Unable to get constructor of " + eventClass, ex);
274             return null;
275         }
276 
277         try
278         {
279             event = (ManagerEvent) constructor
280                     .newInstance(new Object[]{source});
281         }
282         catch (Exception ex)
283         {
284             logger.error("Unable to create new instance of " + eventClass, ex);
285             return null;
286         }
287 
288         setAttributes(event, attributes);
289 
290         // ResponseEvents are sent in response to a ManagerAction if the
291         // response contains lots of data. They include the actionId of
292         // the corresponding ManagerAction.
293         if (event instanceof ResponseEvent)
294         {
295             ResponseEvent responseEvent;
296             String actionId;
297 
298             responseEvent = (ResponseEvent) event;
299             actionId = responseEvent.getActionId();
300             if (actionId != null)
301             {
302                 responseEvent.setActionId(Util.stripInternalActionId(actionId));
303                 responseEvent.setInternalActionId(Util
304                         .getInternalActionId(actionId));
305             }
306         }
307 
308         return event;
309     }
310 
311     private void setAttributes(ManagerEvent event, Map attributes)
312     {
313         Map setters;
314 
315         setters = getSetters(event.getClass());
316 
317         Iterator i = attributes.keySet().iterator();
318         while (i.hasNext())
319         {
320             String name;
321             Object value;
322             Class dataType;
323             Method setter;
324 
325             name = (String) i.next();
326 
327             if ("event".equals(name))
328             {
329                 continue;
330             }
331 
332             /*
333              * The source property needs special handling as it is already
334              * defined in java.util.EventObject (the base class of
335              * ManagerEvent), so we have to translate it.
336              */
337             if ("source".equals(name))
338             {
339                 setter = (Method) setters.get("src");
340             }
341             else
342             {
343                 setter = (Method) setters.get(stripIllegalCharacters(name));
344             }
345 
346             if (setter == null)
347             {
348                 logger.error("Unable to set property '" + name + "' on "
349                         + event.getClass() + ": no setter");
350                 continue;
351             }
352 
353             dataType = setter.getParameterTypes()[0];
354 
355             if (dataType == Boolean.class)
356             {
357                 value = new Boolean(AstUtil.isTrue((String) attributes
358                         .get(name)));
359             }
360             else if (dataType.isAssignableFrom(String.class))
361             {
362                 value = attributes.get(name);
363             }
364             else
365             {
366                 try
367                 {
368                     Constructor constructor = dataType
369                             .getConstructor(new Class[]{String.class});
370                     value = constructor.newInstance(new Object[]{attributes
371                             .get(name)});
372                 }
373                 catch (Exception e)
374                 {
375                     logger.error("Unable to convert value '"
376                             + attributes.get(name) + "' of property '" + name
377                             + "' on " + event.getClass() + " to required type "
378                             + dataType, e);
379                     continue;
380                 }
381             }
382 
383             try
384             {
385                 setter.invoke(event, new Object[]{value});
386             }
387             catch (Exception e)
388             {
389                 logger.error("Unable to set property '" + name + "' on "
390                         + event.getClass(), e);
391                 continue;
392             }
393         }
394     }
395 
396     /***
397      * Strips all illegal charaters from the given lower case string.
398      * 
399      * @param s the original string
400      * @return the string with all illegal characters stripped
401      */
402     private String stripIllegalCharacters(String s)
403     {
404         char c;
405         boolean needsStrip = false;
406         StringBuffer sb;
407 
408         if (s == null)
409         {
410             return null;
411         }
412 
413         for (int i = 0; i < s.length(); i++)
414         {
415             c = s.charAt(i);
416             if (c >= '0' && c <= '9')
417             {
418                 continue;
419             }
420             else if (c >= 'a' && c <= 'z')
421             {
422                 continue;
423             }
424             else
425             {
426                 needsStrip = true;
427                 break;
428             }
429         }
430 
431         if (!needsStrip)
432         {
433             return s;
434         }
435 
436         sb = new StringBuffer(s.length());
437         for (int i = 0; i < s.length(); i++)
438         {
439             c = s.charAt(i);
440             if (c >= '0' && c <= '9')
441             {
442                 sb.append(c);
443             }
444             else if (c >= 'a' && c <= 'z')
445             {
446                 sb.append(c);
447             }
448         }
449 
450         return sb.toString();
451     }
452 
453     private Map getSetters(Class clazz)
454     {
455         Map accessors = new HashMap();
456         Method[] methods = clazz.getMethods();
457 
458         for (int i = 0; i < methods.length; i++)
459         {
460             String name;
461             String methodName;
462             Method method = methods[i];
463 
464             methodName = method.getName();
465             if (!methodName.startsWith("set"))
466             {
467                 continue;
468             }
469 
470             // skip methods with != 1 parameters
471             if (method.getParameterTypes().length != 1)
472             {
473                 continue;
474             }
475 
476             // ok seems to be an accessor
477             name = methodName.substring("set".length()).toLowerCase();
478             accessors.put(name, method);
479         }
480 
481         return accessors;
482     }
483 }