package org.mobicents.servlet.sip.example; import java.io.StringReader; import java.util.Collection; import java.util.Properties; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.log4j.Logger; import org.jdiameter.api.Answer; import org.jdiameter.api.ApplicationId; import org.jdiameter.api.Avp; import org.jdiameter.api.AvpDataException; import org.jdiameter.api.AvpSet; import org.jdiameter.api.Message; import org.jdiameter.api.Request; import org.mobicents.diameter.api.DiameterMessageFactory; import org.mobicents.diameter.api.DiameterProvider; import org.mobicents.diameter.dictionary.AvpDictionary; import org.mobicents.diameter.stack.DiameterListener; import org.mobicents.diameter.stack.DiameterStackMultiplexer; import org.w3c.dom.Document; import org.xml.sax.InputSource; /** * * This class represents a Sh interface client. * * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a> */ public class DiameterShClient implements DiameterProvider, DiameterListener { private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(DiameterShClient.class); private DiameterStackMultiplexer muxMBean = null; private DiameterMessageFactory msgFactory = null; private DiameterProvider provider = null; private String originIP = "127.0.0.1"; private String originPort = "1812"; private String originRealm = "mobicents.org"; private String originHost = null; private String destinationIP = "127.0.0.1"; private String destinationPort = "3868"; private String destinationRealm = "mobicents.org"; private String destinationHost = null; private final long SH_VENDOR_ID = 10415; private final long SH_APPLICATION_ID = 16777217; private String PROPERTIES_FILE = "diameter-openims.properties"; public DiameterShClient() throws InstanceNotFoundException, MBeanException, ReflectionException, NullPointerException, MalformedObjectNameException { ObjectName objectName = new ObjectName("diameter.mobicents:service=DiameterStackMultiplexer"); DiameterListener listener = this; // Create the Application-Id for Sh ApplicationId[] appIds = new ApplicationId[]{ApplicationId.createByAuthAppId( SH_VENDOR_ID, SH_APPLICATION_ID )}; Object[] params = new Object[]{}; String[] signature = new String[]{}; String operation = "getMultiplexerMBean"; MBeanServer server = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0); Object object = server.invoke( objectName, operation, params, signature ); if(object instanceof DiameterStackMultiplexer) muxMBean = (DiameterStackMultiplexer) object; logger.info( "muxMBean == " + muxMBean ); muxMBean.registerListener( listener, appIds ); msgFactory = muxMBean.getMessageFactory(); provider = muxMBean.getProvider(); initializeExample(); } private void initializeExample() { try { Properties props = new Properties(); props.load( this.getClass().getClassLoader().getResourceAsStream("../../META-INF/" + PROPERTIES_FILE) ); this.originIP = props.getProperty( "origin.ip" ) == null ? this.originIP : props.getProperty( "origin.ip" ); this.originPort = props.getProperty( "origin.port" ) == null ? this.originPort : props.getProperty( "origin.port" ); this.originRealm = props.getProperty( "origin.realm" ) == null ? this.originRealm : props.getProperty( "origin.realm" ); this.originHost = props.getProperty( "origin.host" ); this.destinationIP = props.getProperty( "destination.ip" ) == null ? this.destinationIP : props.getProperty( "destination.ip" ); this.destinationPort = props.getProperty( "destination.port" ) == null ? this.destinationPort : props.getProperty( "destination.port" ); this.destinationRealm = props.getProperty( "destination.realm" ) == null ? this.destinationRealm : props.getProperty( "destination.realm" ); this.destinationHost = props.getProperty( "destination.host" ); String usersStr = props.getProperty( "users" ); if(usersStr != null && usersStr.length() > 0) { String[] users = usersStr.split( "," ); // We must wait a while until Diameter Connection is properly established Thread.sleep( 15000 ); logger.info( "Subscribing to Profile Updates from Users " + users.toString() ); for(String user : users) { // Create the SNR for the desired user Request snr = createSubscribeNotificationsRequest(user.trim()); // And send it! Answer ans = (Answer)this.sendMessageSync( snr ); // Check if we succeeded or if we failed if( ans != null && ans.getResultCode() != null && ans.getResultCode().getUnsigned32() == 2001 ) { logger.info( "Successfully subscribed to notifications for user '" + user + "'." ); } else { logger.warn( "Failed subscription to notifications for user '" + user + "'." ); } } } else { logger.warn( "No Users are defined for the example. Nothing will happen..." ); } } catch (Exception e) { logger.error( "Failure reading properties file.", e ); } } public String sendMessage( Message message ) { return this.provider.sendMessage( message ); } public Answer processRequest( Request request ) { try { if(request.getCommandCode() == DiameterShCodes.PUSH_NOTIFICATION_REQUEST) { logger.info( "Push-Notification-Request received.\r\n" + request ); AvpSet avps = request.getAvps(); String userPublicIdentity = avps.getAvp( DiameterShCodes.USER_IDENTITY_AVP ).getGrouped().getAvp( DiameterShCodes.PUBLIC_IDENTITY_AVP ).getUTF8String(); String userData = avps.getAvp( DiameterShCodes.USER_DATA_AVP ).getOctetString(); Collection<MissedCall> mCs = DiameterOpenIMSSipServlet.missedCalls.get( userPublicIdentity ); if(mCs != null && mCs.size() > 0) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = (Document) builder.parse(new InputSource(new StringReader(userData))); String userState = doc.getElementsByTagName("IMSUserState").item(0).getTextContent(); if(userState.equals("1")) { synchronized ( mCs ) { for(MissedCall missedCall : mCs ) { new DiameterOpenIMSSipServlet(); // Send SIP Message DiameterOpenIMSSipServlet.sendSIPMessage( userPublicIdentity, missedCall.getNotification() ); } } // Clear the missed calls for this user mCs.clear(); } } return request.createAnswer( 2001 ); } else { logger.info( "Not Processing unexpected request (Code[" + request.getCommandCode() + "])"); } } catch (Exception e) { logger.error( "", e ); } // We don't deal with this... return null; } public void receivedSuccessMessage( Request request, Answer answer ) { logger.info( "Received success message (Result-Code[" + answer.getResultCode() + "]) for Diameter Request with Session-Id [" + request.getSessionId() + "]" ); } public void timeoutExpired( Request request ) { logger.info( "Timeout expired for Diameter Request with Session-Id [" + request.getSessionId() + "]" ); } public Message sendMessageSync( Message message ) { logger.info( printMessage( message ) ); return this.provider.sendMessageSync( message ); } private String printMessage( Message message ) { String toString = "\r\n" + "+----------------------------------- HEADER ----------------------------------+\r\n" + "| Version................." + message.getVersion() + "\r\n" + "| Command-Flags..........." + "R[" + message.isRequest() + "] P[" + message.isProxiable() + "] " + "E[" + message.isError() + "] T[" + message.isReTransmitted() + "]" + "\r\n" + "| Command-Code............" + message.getCommandCode() + "\r\n" + "| Application-Id.........." + message.getApplicationId() + "\r\n" + "| Hop-By-Hop Identifier..." + message.getHopByHopIdentifier() + "\r\n" + "| End-To-End Identifier..." + message.getEndToEndIdentifier() + "\r\n" + "+------------------------------------ AVPs -----------------------------------+\r\n"; for( Avp avp : message.getAvps() ) { toString += printAvp( avp, "" ); } toString += "+-----------------------------------------------------------------------------+\r\n"; return toString; } private String printAvp(Avp avp, String indent) { Object avpValue = null; String avpString = ""; boolean isGrouped = false; try { String avpType = AvpDictionary.INSTANCE.getAvp( avp.getCode(), avp.getVendorId() ).getType(); if("Integer32".equals(avpType) || "AppId".equals(avpType)) { avpValue = avp.getInteger32(); } else if("Unsigned32".equals(avpType) || "VendorId".equals(avpType)) { avpValue = avp.getUnsigned32(); } else if("Float64".equals(avpType)) { avpValue = avp.getFloat64(); } else if("Integer64".equals(avpType)) { avpValue = avp.getInteger64(); } else if("Time".equals(avpType)) { avpValue = avp.getTime(); } else if("Unsigned64".equals(avpType)) { avpValue = avp.getUnsigned64(); } else if("Grouped".equals(avpType)) { avpValue = "<Grouped>"; isGrouped = true; } else { avpValue = avp.getOctetString().replaceAll( "\r", "" ).replaceAll( "\n", "" ); } } catch (Exception ignore) { try { avpValue = avp.getOctetString().replaceAll( "\r", "" ).replaceAll( "\n", "" ); } catch ( AvpDataException e ) { avpValue = avp.toString(); } } avpString += "| " + indent + "AVP: Code[" + avp.getCode() + "] VendorID[" + avp.getVendorId() + "] Value[" + avpValue + "] Flags[M=" + avp.isMandatory() + ";E=" + avp.isEncrypted() + ";V=" + avp.isVendorId() + "]\r\n"; if(isGrouped) { try { for(Avp subAvp : avp.getGrouped()) { avpString += printAvp( subAvp, indent + " " ); } } catch ( AvpDataException e ) { // Failed to ungroup... ignore then... } } return avpString; } public Request createSubscribeNotificationsRequest(String user) { try { // Create the Subscribe-Notifications-Request // < Subscribe-Notifications-Request > :: = < Diameter Header: 308, REQ, PXY, 16777217 > Request req = (Request) this.msgFactory.createRequest( DiameterShCodes.SUBSCRIBE_NOTIFICATIONS_REQUEST, SH_APPLICATION_ID ); // Make it proxiable (just in case... we never know, what will happen) req.setProxiable( true ); // Obtain the AVPs (should be an empty set) AvpSet avps = req.getAvps(); // < Session-Id > avps.addAvp( Avp.SESSION_ID, ("123sip-servlets456;" + System.currentTimeMillis()).getBytes() ); // { Vendor-Specific-Application-Id } AvpSet vsaiAvp = avps.addGroupedAvp( Avp.VENDOR_SPECIFIC_APPLICATION_ID, true, false ); vsaiAvp.addAvp( Avp.VENDOR_ID, SH_VENDOR_ID, true, false, true ); vsaiAvp.addAvp( Avp.AUTH_APPLICATION_ID, SH_APPLICATION_ID, true, false, true ); // { Auth-Session-State } // 0 == Idle,1 == Pending, 2 == Open, 3 == Disconnected avps.addAvp( Avp.AUTH_SESSION_STATE, 2, true, false ); // { Origin-Host } avps.addAvp( Avp.ORIGIN_HOST, (this.originHost != null ? this.originHost : "aaa://" + this.originIP + ":" + this.originPort).getBytes(), true, false ); // { Origin-Realm } avps.addAvp( Avp.ORIGIN_REALM, this.originRealm.getBytes(), true, false ); // [ Destination-Host ] avps.addAvp( Avp.DESTINATION_HOST, (this.destinationHost != null ? this.destinationHost : "aaa://" + this.destinationIP + ":" + this.destinationPort).getBytes(), true, false ); // { Destination-Realm } avps.addAvp( Avp.DESTINATION_REALM, this.destinationRealm.getBytes(), true, false ); // *[ Supported-Features ] // { User-Identity } AvpSet ui = avps.addGroupedAvp( DiameterShCodes.USER_IDENTITY_AVP, SH_VENDOR_ID, true, false ); ui.addAvp( DiameterShCodes.PUBLIC_IDENTITY_AVP, "sip:" + user.replaceFirst( "sip:", "" ), SH_VENDOR_ID, true, true, false ); // [ Wildcarded-PSI ] // [ Wildcarded-IMPU ] // *[ Service-Indication ] // [ Send-Data-Indication ] // [ Server-Name ] // { Subs-Req-Type } // 0 == Subscribe // 1 == Unsubrscribe avps.addAvp( DiameterShCodes.SUBS_REQ_TYPE_AVP, 0, SH_VENDOR_ID, true, false ); // *{ Data-Reference } // It's enumerated: 0 == Whole data ... 11 == User-State avps.addAvp( DiameterShCodes.DATA_REFERENCE_AVP, 11, SH_VENDOR_ID, true, false ); // [ Identity-Set ] // [ Expiry-Time ] logger.info( "Created Subscribe-Notifications-Request:\r\n" + printMessage( req ) ); return req; } catch (Exception e) { logger.error( "Failure trying to create/send Subscribe-Notifications-Request.", e ); return null; } } }