/*
* Copyright 2010 david varnes.
*
* 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.freeswitch.esl.client.transport.event;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.freeswitch.esl.client.internal.HeaderParser;
import org.freeswitch.esl.client.transport.message.EslHeaders;
import org.freeswitch.esl.client.transport.message.EslMessage;
import org.freeswitch.esl.client.transport.message.EslHeaders.Name;
import org.freeswitch.esl.client.transport.message.EslHeaders.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* FreeSWITCH Event Socket <strong>events</strong> are decoded into this data object.
* <p>
* An ESL event is modelled as a collection of text lines. An event always has several eventHeader
* lines, and optionally may have some eventBody lines. In addition the messageHeaders of the
* original containing {@link EslMessage} which carried the event are also available.
* <p>
* The eventHeader lines are parsed and cached in a map keyed by the eventHeader name string. An event
* is always expected to have an "Event-Name" eventHeader. Commonly used eventHeader names are coded
* in {@link EslEventHeaderNames}
* <p>
* Any eventBody lines are cached in a list.
* <p>
* The messageHeader lines from the original message are cached in a map keyed by {@link EslHeaders.Name}.
*
* @author david varnes
* @see EslEventHeaderNames
*/
public class EslEvent
{
private final Logger log = LoggerFactory.getLogger( this.getClass() );
private final Map<Name,String> messageHeaders;
private final Map<String,String> eventHeaders;
private final List<String> eventBody;
private boolean decodeEventHeaders = true;
public EslEvent( EslMessage rawMessage )
{
this( rawMessage, false );
}
public EslEvent( EslMessage rawMessage, boolean parseCommandReply )
{
messageHeaders = rawMessage.getHeaders();
eventHeaders = new HashMap<String,String>( rawMessage.getBodyLines().size() );
eventBody = new ArrayList<String>();
// plain or xml body
if ( rawMessage.getContentType().equals( Value.TEXT_EVENT_PLAIN ) )
{
parsePlainBody( rawMessage.getBodyLines() );
}
else if ( rawMessage.getContentType().equals( Value.TEXT_EVENT_XML ) )
{
throw new IllegalStateException( "XML events are not yet supported" );
}
else if ( rawMessage.getContentType().equals( Value.COMMAND_REPLY ) && parseCommandReply )
{
parsePlainBody( rawMessage.getBodyLines() );
}
else
{
throw new IllegalStateException( "Unexpected EVENT content-type: " +
rawMessage.getContentType() );
}
}
/**
* The message headers of the original ESL message from which this event was decoded.
* The message headers are stored in a map keyed by {@link EslHeaders.Name}. The string mapped value
* is the parsed content of the header line (ie, it does not include the header name).
*
* @return map of header values
*/
public Map<Name,String> getMessageHeaders()
{
return messageHeaders;
}
/**
* The event headers of this event. The headers are parsed and stored in a map keyed by the string
* name of the header, and the string mapped value is the parsed content of the event header line
* (ie, it does not include the header name).
*
* @return map of event header values
*/
public Map<String, String> getEventHeaders()
{
return eventHeaders;
}
/**
* Any event body lines that were present in the event.
*
* @return list of decoded event body lines, may be an empty list.
*/
public List<String> getEventBodyLines()
{
return eventBody;
}
/**
* Convenience method.
*
* @return the string value of the event header "Event-Name"
*/
public String getEventName()
{
return getEventHeaders().get( EslEventHeaderNames.EVENT_NAME );
}
/**
* Convenience method.
*
* @return the string value of the event header "Event-Subclass"
*/
public String getEventSubclass()
{
String subClass = getEventHeaders().get( EslEventHeaderNames.EVENT_SUBCLASS );
if(subClass == null){
return "NONE";
}
return subClass;
}
/**
* Convenience method.
*
* @return long value of the event header "Event-Date-Timestamp"
*/
public long getEventDateTimestamp()
{
return Long.valueOf( getEventHeaders().get( EslEventHeaderNames.EVENT_DATE_TIMESTAMP ) );
}
/**
* Convenience method.
*
* @return long value of the event header "Event-Date-Local"
*/
public String getEventDateLocal()
{
return getEventHeaders().get( EslEventHeaderNames.EVENT_DATE_LOCAL );
}
/**
* Convenience method.
*
* @return long value of the event header "Event-Date-GMT"
*/
public String getEventDateGmt()
{
return getEventHeaders().get( EslEventHeaderNames.EVENT_DATE_GMT );
}
/**
* Convenience method.
*
* @return true if the eventBody list is not empty.
*/
public boolean hasEventBody()
{
return ! eventBody.isEmpty();
}
private void parsePlainBody( final List<String> rawBodyLines )
{
boolean isEventBody = false;
for ( String rawLine : rawBodyLines )
{
if ( ! isEventBody )
{
// split the line
String[] headerParts = HeaderParser.splitHeader( rawLine );
if ( decodeEventHeaders )
{
try
{
String decodedValue = URLDecoder.decode( headerParts[1], "UTF-8" );
log.trace( "decoded from: [{}]", headerParts[1] );
log.trace( "decoded to: [{}]", decodedValue );
eventHeaders.put( headerParts[0], decodedValue );
}
catch ( UnsupportedEncodingException e )
{
log.warn( "Could not URL decode [{}]", headerParts[1] );
eventHeaders.put( headerParts[0], headerParts[1] );
}
}
else
{
eventHeaders.put( headerParts[0], headerParts[1] );
}
if ( headerParts[0].equals( EslEventHeaderNames.CONTENT_LENGTH ) )
{
// the remaining lines will be considered body lines
isEventBody = true;
}
}
else
{
// ignore blank line (always is one following the content-length
if ( rawLine.length() > 0 )
{
eventBody.add( rawLine );
}
}
}
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder( "EslEvent: name=[" );
sb.append( getEventName() );
sb.append( "] subclass=[");
sb.append( getEventSubclass() );
sb.append( "] headers=" );
sb.append( messageHeaders.size() );
sb.append( ", eventHeaders=" );
sb.append( eventHeaders.size() );
sb.append( ", eventBody=" );
sb.append( eventBody.size() );
sb.append( " lines." );
return sb.toString();
}
}