View Javadoc

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