package org.mobicents.diameter.stack; import java.io.InputStream; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import org.jboss.system.ServiceMBeanSupport; import org.jdiameter.api.Answer; import org.jdiameter.api.ApplicationAlreadyUseException; import org.jdiameter.api.ApplicationId; import org.jdiameter.api.Avp; import org.jdiameter.api.Configuration; import org.jdiameter.api.EventListener; import org.jdiameter.api.InternalException; import org.jdiameter.api.Message; import org.jdiameter.api.Network; import org.jdiameter.api.NetworkReqListener; import org.jdiameter.api.Request; import org.jdiameter.api.ResultCode; import org.jdiameter.api.Session; import org.jdiameter.api.Stack; import org.jdiameter.server.impl.StackImpl; import org.jdiameter.server.impl.helpers.XMLConfiguration; import org.mobicents.diameter.api.DiameterMessageFactory; import org.mobicents.diameter.api.DiameterProvider; import org.mobicents.diameter.dictionary.AvpDictionary; public class DiameterStackMultiplexer extends ServiceMBeanSupport implements DiameterStackMultiplexerMBean, DiameterProvider, NetworkReqListener, EventListener<Request, Answer>, DiameterMessageFactory { protected Stack stack = null; protected HashMap<DiameterListener, Collection<ApplicationId>> listenerToAppId = new HashMap<DiameterListener, Collection<ApplicationId>>(3); protected HashMap<Long, DiameterListener> appIdToListener = new HashMap<Long, DiameterListener>(3); // This is for synch protected ReentrantLock lock = new ReentrantLock(); protected DiameterProvider provider; // ===== STACK MANAGEMENT ===== private void initStack() throws Exception { InputStream is = null; try { // Create and configure stack this.stack = new StackImpl(); // Get configuration String configFile = "jdiameter-config.xml"; is = this.getClass().getClassLoader().getResourceAsStream("config/" + configFile); // Load the configuration Configuration config = new XMLConfiguration(is); this.stack.init(config); Network network = stack.unwrap(Network.class); Set<ApplicationId> appIds = stack.getMetaData().getLocalPeer().getCommonApplications(); log.info("Diameter Stack Mux :: Supporting " + appIds.size() + " applications."); //network.addNetworkReqListener(this, ApplicationId.createByAccAppId( 193, 19302 )); for (ApplicationId appId : appIds) { log.info("Diameter Stack Mux :: Adding Listener for [" + appId + "]."); network.addNetworkReqListener(this, appId); if( appId.getAcctAppId() != ApplicationId.UNDEFINED_VALUE ) { this.appIdToListener.put(appId.getAcctAppId(), null); } else if( appId.getAuthAppId() != ApplicationId.UNDEFINED_VALUE ) { this.appIdToListener.put(appId.getAuthAppId(), null); } } try { log.info( "Parsing AVP Dictionary file..." ); AvpDictionary.INSTANCE.parseDictionary( AvpDictionary.class.getResourceAsStream("dictionary.xml") ); log.info( "AVP Dictionary file successfuly parsed!" ); } catch ( Exception e ) { log.error( "Error while parsing dictionary file.", e ); } this.stack.start(); } finally { if (is != null) is.close(); is = null; } log.info("Diameter Stack Mux :: Successfully initialized stack."); } private void stopStack() throws Exception { try { log.info("Stopping Diameter Mux Stack..."); stack.stop(10, TimeUnit.SECONDS); log.info("Diameter Mux Stack Stopped Successfully."); } catch (Exception e) { log.error( "Failure while stopping stack", e ); } stack.destroy(); } private DiameterListener findListener(Message message) { Set<ApplicationId> appIds = message.getApplicationIdAvps(); if( appIds.size() > 0 ) { for(ApplicationId appId : appIds) { log.info( "Diameter Stack Mux :: findListener :: AVP AppId [" + appId + "]" ); DiameterListener listener; Long appIdValue = appId.getAcctAppId() != ApplicationId.UNDEFINED_VALUE ? appId.getAcctAppId() : appId.getAuthAppId(); if( (listener = this.appIdToListener.get(appIdValue)) != null ) { log.info( "Diameter Stack Mux :: findListener :: Found Listener [" + listener + "]" ); return listener; } } } else { Long appId = message.getApplicationId(); log.info( "Diameter Stack Mux :: findListener :: Header AppId [" + appId + "]" ); DiameterListener listener; if( (listener = this.appIdToListener.get(appId)) != null ) { log.info( "Diameter Stack Mux :: findListener :: Found Listener [" + listener + "]" ); return listener; } } log.info( "Diameter Stack Mux :: findListener :: No Listener Found." ); return null; } // ===== NetworkReqListener IMPLEMENTATION ===== public Answer processRequest( Request request ) { log.info( "Diameter Stack Mux :: processRequest :: Command-Code [" + request.getCommandCode() + "]" ); DiameterListener listener = findListener( request ); if( listener != null ) { return listener.processRequest( request ); } else { try { Answer answer = request.createAnswer( ResultCode.APPLICATION_UNSUPPORTED ); //this.stack.getSessionFactory().getNewRawSession().send(answer); return answer; } catch ( Exception e ) { log.error( "", e ); } } return null; } // ===== EventListener<Request, Answer> IMPLEMENTATION ===== public void receivedSuccessMessage( Request request, Answer answer ) { DiameterListener listener = findListener( request ); if( listener != null ) { listener.receivedSuccessMessage( request, answer ); } } public void timeoutExpired( Request request ) { DiameterListener listener = findListener( request ); if( listener != null ) { listener.timeoutExpired( request ); } } // ===== SERVICE LIFECYCLE MANAGEMENT ===== @Override protected void startService() throws Exception { super.startService(); initStack(); } @Override protected void stopService() throws Exception { super.stopService(); stopStack(); } public String sendMessage( Message message ) { try { Avp sessionId = null; Session session = null; if((sessionId = message.getAvps().getAvp(Avp.SESSION_ID)) == null) { session = stack.getSessionFactory().getNewSession(); } else { session = stack.getSessionFactory().getNewSession( sessionId.getUTF8String() ); } session.send( message ); return session.getSessionId(); } catch (Exception e) { log.error( "", e ); } return null; } public Message sendMessageSync( Message message ) { try { Avp sessionId = null; Session session = null; if((sessionId = message.getAvps().getAvp(Avp.SESSION_ID)) == null) { session = stack.getSessionFactory().getNewSession(); } else { session = stack.getSessionFactory().getNewSession( sessionId.getUTF8String() ); } Future<Message> answer = session.send( message ); return answer.get(); } catch (Exception e) { log.error( "", e ); } return null; } public Message createMessage( boolean isRequest, int commandCode, long applicationId ) { try { Message message = this.stack.getSessionFactory().getNewRawSession().createMessage( commandCode, ApplicationId.createByAccAppId( applicationId ), new Avp[]{} ); message.setRequest( isRequest ); return message; } catch ( Exception e ) { log.error( "Failure while creating message.", e ); } return null; } public Message createRequest( int commandCode, long applicationId ) { return createMessage( true, commandCode, applicationId ); } public Message createAnswer( int commandCode, long applicationId ) { return createMessage( false, commandCode, applicationId ); } // ===== MBEAN OPERATIONS ===== public DiameterStackMultiplexerMBean getMultiplexerMBean() { return this; } public DiameterMessageFactory getMessageFactory() { return this; } public DiameterProvider getProvider() { return this; } public Stack getStack() { return new DiameterStackProxy(this.stack); } public void registerListener( DiameterListener listener, ApplicationId[] appIds) throws IllegalStateException { if(listener == null) { log.warn( "Trying to register a null Listener. Give up..." ); return; } int curAppIdIndex = 0; try { lock.lock(); // Register the selected appIds in the stack Network network = stack.unwrap(Network.class); log.info("Diameter Stack Mux :: Registering " + appIds.length + " applications."); for (; curAppIdIndex < appIds.length; curAppIdIndex++) { ApplicationId appId = appIds[curAppIdIndex]; log.info("Diameter Stack Mux :: Adding Listener for [" + appId + "]."); network.addNetworkReqListener(this, appId); if( appId.getAcctAppId() != ApplicationId.UNDEFINED_VALUE ) { this.appIdToListener.put(appId.getAcctAppId(), listener); } else if( appId.getAuthAppId() != ApplicationId.UNDEFINED_VALUE ) { this.appIdToListener.put(appId.getAuthAppId(), listener); } } // And add the listener and it's holder Collection<ApplicationId> registeredAppIds = this.listenerToAppId.get( listener ); // Merge the existing (if any) with new. if(registeredAppIds != null) { registeredAppIds.addAll( Arrays.asList(appIds) ); } else { this.listenerToAppId.put( listener, Arrays.asList(appIds) ); } } catch (ApplicationAlreadyUseException aaue) { // Let's remove what we've done so far... try { Network network = stack.unwrap(Network.class); for (; curAppIdIndex >= 0; curAppIdIndex--) { // Remove the app id from map this.appIdToListener.remove(appIds[curAppIdIndex]); // Unregister it from stack listener network.removeNetworkReqListener(appIds[curAppIdIndex]); } } catch (Exception e) { log.error( "", e ); } } catch (Exception e) { log.error( "", e ); } finally { lock.unlock(); } } public void unregisterListener( DiameterListener listener ) { log.info( "Diameter Stack Mux :: unregisterListener :: Listener [" + listener + "]" ); if(listener == null) { log.warn( "Diameter Stack Mux :: unregisterListener :: Trying to unregister a null Listener. Give up..." ); return; } try { lock.lock(); Collection<ApplicationId> appIds = this.listenerToAppId.remove(listener); if(appIds == null) { log.warn( "Diameter Stack Mux :: unregisterListener :: Listener has no App-Ids registered. Give up..." ); return; } Network network = stack.unwrap(Network.class); for (ApplicationId appId : appIds) { try { log.info( "Diameter Stack Mux :: unregisterListener :: Unregistering AppId [" + appId + "]" ); // Remove the appid from map this.appIdToListener.remove(appId); // and unregister the listener from stack network.removeNetworkReqListener(appId); } catch (Exception e) { log.error( "", e ); } } } catch (InternalException ie) { log.error( "", ie ); } finally { lock.unlock(); } } }