/* * Copyright 2004-2006 Stefan Reuter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.asteriskjava.manager.internal; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.asteriskjava.manager.event.*; import org.asteriskjava.util.AstUtil; import org.asteriskjava.util.Log; import org.asteriskjava.util.LogFactory; import org.asteriskjava.util.ReflectionUtil; /** * Default implementation of the EventBuilder interface. * * @author srt * @version $Id$ * @see org.asteriskjava.manager.event.ManagerEvent */ class EventBuilderImpl implements EventBuilder { private final Log logger = LogFactory.getLog(getClass()); private Map<String, Class> registeredEventClasses; EventBuilderImpl() { this.registeredEventClasses = new HashMap<String, Class>(); registerBuiltinEventClasses(); } @SuppressWarnings("deprecation") private void registerBuiltinEventClasses() { registerEventClass(AgentCallbackLoginEvent.class); registerEventClass(AgentCallbackLogoffEvent.class); registerEventClass(AgentCalledEvent.class); registerEventClass(AgentConnectEvent.class); registerEventClass(AgentCompleteEvent.class); registerEventClass(AgentDumpEvent.class); registerEventClass(AgentLoginEvent.class); registerEventClass(AgentLogoffEvent.class); registerEventClass(AgentsEvent.class); registerEventClass(AgentsCompleteEvent.class); registerEventClass(AgiExecEvent.class); registerEventClass(AsyncAgiEvent.class); registerEventClass(AlarmEvent.class); registerEventClass(AlarmClearEvent.class); registerEventClass(BridgeEvent.class); registerEventClass(CdrEvent.class); registerEventClass(ChannelReloadEvent.class); registerEventClass(ChannelUpdateEvent.class); registerEventClass(ConferenceDTMFEvent.class); registerEventClass(DbGetResponseEvent.class); registerEventClass(DialEvent.class); registerEventClass(DndStateEvent.class); registerEventClass(DtmfEvent.class); registerEventClass(ExtensionStatusEvent.class); registerEventClass(FaxReceivedEvent.class); registerEventClass(HangupEvent.class); registerEventClass(HoldedCallEvent.class); registerEventClass(HoldEvent.class); registerEventClass(JoinEvent.class); registerEventClass(LeaveEvent.class); registerEventClass(LinkEvent.class); registerEventClass(LogChannelEvent.class); registerEventClass(MeetMeEndEvent.class); registerEventClass(MeetMeJoinEvent.class); registerEventClass(MeetMeLeaveEvent.class); registerEventClass(MeetMeMuteEvent.class); registerEventClass(MeetMeTalkingEvent.class); registerEventClass(MeetMeTalkingRequestEvent.class); registerEventClass(MeetMeStopTalkingEvent.class); registerEventClass(MessageWaitingEvent.class); registerEventClass(MonitorStartEvent.class); registerEventClass(MonitorStopEvent.class); registerEventClass(NewAccountCodeEvent.class); registerEventClass(NewCallerIdEvent.class); registerEventClass(NewChannelEvent.class); registerEventClass(NewExtenEvent.class); registerEventClass(NewStateEvent.class); registerEventClass(OriginateFailureEvent.class); registerEventClass(OriginateSuccessEvent.class); registerEventClass(OriginateResponseEvent.class); registerEventClass(ParkedCallGiveUpEvent.class); registerEventClass(ParkedCallEvent.class); registerEventClass(ParkedCallTimeOutEvent.class); registerEventClass(ParkedCallsCompleteEvent.class); registerEventClass(PeerEntryEvent.class); registerEventClass(PeerlistCompleteEvent.class); registerEventClass(PeerStatusEvent.class); registerEventClass(QueueCallerAbandonEvent.class); registerEventClass(QueueEntryEvent.class); registerEventClass(QueueMemberAddedEvent.class); registerEventClass(QueueMemberEvent.class); registerEventClass(QueueMemberPausedEvent.class); registerEventClass(QueueMemberPenaltyEvent.class); registerEventClass(QueueMemberRemovedEvent.class); registerEventClass(QueueMemberStatusEvent.class); registerEventClass(QueueParamsEvent.class); registerEventClass(QueueStatusCompleteEvent.class); registerEventClass(QueueSummaryCompleteEvent.class); registerEventClass(QueueSummaryEvent.class); registerEventClass(RegistryEvent.class); registerEventClass(ReloadEvent.class); registerEventClass(RenameEvent.class); registerEventClass(ShutdownEvent.class); registerEventClass(StatusEvent.class); registerEventClass(StatusCompleteEvent.class); registerEventClass(UnholdEvent.class); registerEventClass(UnlinkEvent.class); registerEventClass(UnparkedCallEvent.class); registerEventClass(VoicemailUserEntryCompleteEvent.class); registerEventClass(VoicemailUserEntryEvent.class); registerEventClass(ZapShowChannelsEvent.class); registerEventClass(ZapShowChannelsCompleteEvent.class); } public final void registerEventClass(Class clazz) throws IllegalArgumentException { String className; String eventType; className = clazz.getName(); eventType = className.substring(className.lastIndexOf('.') + 1).toLowerCase(Locale.ENGLISH); if (eventType.endsWith("event")) { eventType = eventType.substring(0, eventType.length() - "event".length()); } if (UserEvent.class.isAssignableFrom(clazz) && !eventType.startsWith("userevent")) { eventType = "userevent" + eventType; } registerEventClass(eventType, clazz); } /** * Registers a new event class for the event given by eventType. * * @param eventType the name of the event to register the class for. For * example "Join". * @param clazz the event class to register, must extend * {@link ManagerEvent}. * @throws IllegalArgumentException if clazz is not a valid event class. */ @SuppressWarnings("unchecked") public final void registerEventClass(String eventType, Class clazz) throws IllegalArgumentException { Constructor defaultConstructor; if (!ManagerEvent.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(clazz + " is not a ManagerEvent"); } if ((clazz.getModifiers() & Modifier.ABSTRACT) != 0) { throw new IllegalArgumentException(clazz + " is abstract"); } try { defaultConstructor = clazz.getConstructor(new Class[]{Object.class}); } catch (NoSuchMethodException ex) { throw new IllegalArgumentException(clazz + " has no usable constructor"); } if ((defaultConstructor.getModifiers() & Modifier.PUBLIC) == 0) { throw new IllegalArgumentException(clazz + " has no public default constructor"); } registeredEventClasses.put(eventType.toLowerCase(), clazz); logger.debug("Registered event type '" + eventType + "' (" + clazz + ")"); } @SuppressWarnings("unchecked") public ManagerEvent buildEvent(Object source, Map<String, String> attributes) { ManagerEvent event; String eventType; Class eventClass; Constructor constructor; if (attributes.get("event") == null) { logger.error("No event event type in properties"); return null; } eventType = attributes.get("event").toLowerCase(); // Change in Asterisk 1.4 where the name of the UserEvent is sent as property instead // of the event name (AJ-48) if ("userevent".equals(eventType)) { String userEventType; if (attributes.get("userevent") == null) { logger.error("No user event type in properties"); return null; } userEventType = attributes.get("userevent").toLowerCase(); eventType = eventType + userEventType; } eventClass = registeredEventClasses.get(eventType); if (eventClass == null) { logger.info("No event class registered for event type '" + eventType + "', attributes: " + attributes + ". Please report at http://jira.reucon.org/browse/AJ"); return null; } try { constructor = eventClass.getConstructor(new Class[]{Object.class}); } catch (NoSuchMethodException ex) { logger.error("Unable to get constructor of " + eventClass.getName(), ex); return null; } try { event = (ManagerEvent) constructor.newInstance(source); } catch (Exception ex) { logger.error("Unable to create new instance of " + eventClass.getName(), ex); return null; } setAttributes(event, attributes); // ResponseEvents are sent in response to a ManagerAction if the // response contains lots of data. They include the actionId of // the corresponding ManagerAction. if (event instanceof ResponseEvent) { ResponseEvent responseEvent; String actionId; responseEvent = (ResponseEvent) event; actionId = responseEvent.getActionId(); if (actionId != null) { responseEvent.setActionId(ManagerUtil.stripInternalActionId(actionId)); responseEvent.setInternalActionId(ManagerUtil.getInternalActionId(actionId)); } } return event; } @SuppressWarnings("unchecked") private void setAttributes(ManagerEvent event, Map<String, String> attributes) { Map<String, Method> setters; setters = ReflectionUtil.getSetters(event.getClass()); for (String name : attributes.keySet()) { Object value; Class dataType; Method setter; if ("event".equals(name)) { continue; } /* * The source property needs special handling as it is already * defined in java.util.EventObject (the base class of * ManagerEvent), so we have to translate it. */ if ("source".equals(name)) { setter = setters.get("src"); } else { setter = setters.get(stripIllegalCharacters(name)); } // it seems silly to warn if it's a user event -- maybe it was intentional if (setter == null && !(event instanceof UserEvent)) { logger.warn("Unable to set property '" + name + "' to '" + attributes.get(name) + "' on " + event.getClass().getName() + ": no setter. Please report at http://jira.reucon.org/browse/AJ"); } if (setter == null) { continue; } dataType = setter.getParameterTypes()[0]; if (dataType == Boolean.class) { value = AstUtil.isTrue(attributes.get(name)); } else if (dataType.isAssignableFrom(String.class)) { value = attributes.get(name); } else { try { Constructor constructor = dataType.getConstructor(new Class[]{String.class}); value = constructor.newInstance(attributes.get(name)); } catch (Exception e) { logger.error("Unable to convert value '" + attributes.get(name) + "' of property '" + name + "' on " + event.getClass().getName() + " to required type " + dataType, e); continue; } } try { setter.invoke(event, value); } catch (Exception e) { logger.error("Unable to set property '" + name + "' to '" + attributes.get(name) + "' on " + event.getClass().getName(), e); } } } /** * Strips all illegal charaters from the given lower case string. * * @param s the original string * @return the string with all illegal characters stripped */ private String stripIllegalCharacters(String s) { char c; boolean needsStrip = false; StringBuffer sb; if (s == null) { return null; } for (int i = 0; i < s.length(); i++) { c = s.charAt(i); if (c >= '0' && c <= '9') { // continue } else if (c >= 'a' && c <= 'z') { // continue } else { needsStrip = true; break; } } if (!needsStrip) { return s; } sb = new StringBuffer(s.length()); for (int i = 0; i < s.length(); i++) { c = s.charAt(i); if (c >= '0' && c <= '9') { sb.append(c); } else if (c >= 'a' && c <= 'z') { sb.append(c); } } return sb.toString(); } }