Overview
Mobicents is an open-source Java-based SIP (Session Initiation Protocol) server that provides VoIP and real-time communication capabilities. It's part of the Restcomm project and supports SIP servlets for building telecom applications.
1. Setup and Dependencies
Maven Dependencies
<!-- pom.xml --> <properties> <mobicents.version>3.0.0.FINAL</mobicents.version> <sip.servlet.version>3.0.0.FINAL</sip.servlet.version> </properties> <dependencies> <!-- Mobicents SIP Servlets --> <dependency> <groupId>org.mobicents.servlet.sip</groupId> <artifactId>sip-servlets-core-api</artifactId> <version>${sip.servlet.version}</version> </dependency> <dependency> <groupId>org.mobicents.servlet.sip</groupId> <artifactId>sip-servlets-core-impl</artifactId> <version>${sip.servlet.version}</version> </dependency> <!-- SIP API --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.sip</groupId> <artifactId>jain-sip-api</artifactId> <version>1.2.1.4</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!-- Utilities --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1.1-jre</version> </dependency> </dependencies> Web Application Configuration
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>Mobicents SIP Server</display-name> <!-- SIP Servlets Configuration --> <servlet> <servlet-name>sip-server</servlet-name> <servlet-class> org.mobicents.servlet.sip.startup.SipServletContextListener </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- SIP Servlet Mapping --> <servlet-mapping> <servlet-name>sip-server</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <!-- Main SIP Servlet --> <servlet> <servlet-name>BasicSipServlet</servlet-name> <servlet-class>com.yourapp.sip.BasicSipServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>BasicSipServlet</servlet-name> <url-pattern>/sip/*</url-pattern> </servlet-mapping> <!-- Concurrent Utilities --> <listener> <listener-class> org.mobicents.servlet.sip.startup.SipConnectorListener </listener-class> </listener> </web-app>
sip.xml
<?xml version="1.0" encoding="UTF-8"?> <sip-app xmlns="http://www.jcp.org/xml/ns/sipservlet" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.jcp.org/xml/ns/sipservlet http://www.jcp.org/xml/ns/sipservlet/sip-app_1_1.xsd" version="1.1"> <app-name>Mobicents SIP Application</app-name> <display-name>Basic SIP Server</display-name> <servlet> <servlet-name>BasicSipServlet</servlet-name> <servlet-class>com.yourapp.sip.BasicSipServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>BasicSipServlet</servlet-name> <pattern> <equal> <var>request.method</var> <value>INVITE</value> </equal> </pattern> </servlet-mapping> <servlet-mapping> <servlet-name>BasicSipServlet</servlet-name> <pattern> <equal> <var>request.method</var> <value>BYE</value> </equal> </pattern> </servlet-mapping> <servlet-mapping> <servlet-name>BasicSipServlet</servlet-name> <pattern> <equal> <var>request.method</var> <value>REGISTER</value> </equal> </pattern> </servlet-mapping> <servlet-mapping> <servlet-name>BasicSipServlet</servlet-name> <pattern> <equal> <var>request.method</var> <value>ACK</value> </equal> </pattern> </servlet-mapping> </sip-app>
2. Basic SIP Servlet Implementation
Core SIP Servlet
package com.yourapp.sip; import org.mobicents.servlet.sip.RestcommSipServlet; import org.mobicents.servlet.sip.annotation.SipApplication; import org.mobicents.servlet.sip.annotation.SipApplicationKey; import org.mobicents.servlet.sip.annotation.SipListener; import org.mobicents.servlet.sip.message.SipFactoryImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.sip.*; import java.io.IOException; import java.text.ParseException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @SipApplication( name = "BasicSipApplication", displayName = "Basic SIP Server Application", applicationKey = @SipApplicationKey( key = "BasicSipApp", appName = "BasicSipApplication" ) ) @SipListener public class BasicSipServlet extends SipServlet { private static final long serialVersionUID = 1L; private static final Logger logger = LoggerFactory.getLogger(BasicSipServlet.class); private SipFactory sipFactory; private Map<String, SipSession> activeSessions; private Map<String, UserRegistration> registeredUsers; @Override public void init() { this.sipFactory = (SipFactory) getServletContext().getAttribute(SIP_FACTORY); this.activeSessions = new ConcurrentHashMap<>(); this.registeredUsers = new ConcurrentHashMap<>(); logger.info("Basic SIP Servlet initialized successfully"); } @Override protected void doRegister(SipServletRequest request) throws ServletException, IOException { try { String fromUser = getUsernameFromAddress(request.getFrom().getURI().toString()); String contact = request.getAddressHeader("Contact").getURI().toString(); logger.info("REGISTER request from: {}, contact: {}", fromUser, contact); // Register user registeredUsers.put(fromUser, new UserRegistration(fromUser, contact, System.currentTimeMillis())); // Create OK response SipServletResponse response = request.createResponse(SipServletResponse.SC_OK); response.setAddressHeader("Contact", request.getAddressHeader("Contact")); response.addHeader("Expires", "3600"); response.send(); logger.info("User {} registered successfully", fromUser); } catch (Exception e) { logger.error("Error processing REGISTER request", e); request.createResponse(SipServletResponse.SC_SERVER_INTERNAL_ERROR).send(); } } @Override protected void doInvite(SipServletRequest request) throws ServletException, IOException { try { String fromUser = getUsernameFromAddress(request.getFrom().getURI().toString()); String toUser = getUsernameFromAddress(request.getTo().getURI().toString()); logger.info("INVITE request from {} to {}", fromUser, toUser); // Check if target user is registered UserRegistration targetUser = registeredUsers.get(toUser); if (targetUser == null) { logger.warn("User {} not found", toUser); request.createResponse(SipServletResponse.SC_NOT_FOUND).send(); return; } // Create provisional response SipServletResponse trying = request.createResponse(SipServletResponse.SC_TRYING); trying.send(); // Create ringing response SipServletResponse ringing = request.createResponse(SipServletResponse.SC_RINGING); ringing.send(); // Store the session String sessionId = generateSessionId(); activeSessions.put(sessionId, request.getSession()); // For demo purposes, auto-answer after 2 seconds new Thread(() -> { try { Thread.sleep(2000); SipServletResponse ok = request.createResponse(SipServletResponse.SC_OK); ok.setContent(request.getContent(), request.getContentType()); ok.send(); logger.info("Call established between {} and {}", fromUser, toUser); } catch (Exception e) { logger.error("Error establishing call", e); } }).start(); } catch (Exception e) { logger.error("Error processing INVITE request", e); request.createResponse(SipServletResponse.SC_SERVER_INTERNAL_ERROR).send(); } } @Override protected void doAck(SipServletRequest request) throws ServletException, IOException { String sessionId = request.getSession().getId(); logger.info("ACK received for session: {}", sessionId); // Call is now established // You can start media session here } @Override protected void doBye(SipServletRequest request) throws ServletException, IOException { try { String sessionId = request.getSession().getId(); logger.info("BYE request for session: {}", sessionId); // Remove session activeSessions.remove(sessionId); // Send OK response SipServletResponse response = request.createResponse(SipServletResponse.SC_OK); response.send(); logger.info("Call terminated for session: {}", sessionId); } catch (Exception e) { logger.error("Error processing BYE request", e); request.createResponse(SipServletResponse.SC_SERVER_INTERNAL_ERROR).send(); } } @Override protected void doCancel(SipServletRequest request) throws ServletException, IOException { String sessionId = request.getSession().getId(); logger.info("CANCEL request for session: {}", sessionId); // Remove session and send appropriate responses activeSessions.remove(sessionId); request.createResponse(SipServletResponse.SC_OK).send(); } @Override protected void doError(SipServletRequest request) throws ServletException, IOException { logger.error("SIP Error received: {}", request.getStatus()); logger.error("Error message: {}", request.getReasonPhrase()); } // Utility methods private String getUsernameFromAddress(String address) { if (address.contains(":")) { return address.split(":")[1].split("@")[0]; } return address.split("@")[0].replace("sip:", ""); } private String generateSessionId() { return "sess_" + System.currentTimeMillis() + "_" + Math.random(); } public Map<String, UserRegistration> getRegisteredUsers() { return new HashMap<>(registeredUsers); } public Map<String, SipSession> getActiveSessions() { return new HashMap<>(activeSessions); } @Override public void destroy() { logger.info("Basic SIP Servlet shutting down"); activeSessions.clear(); registeredUsers.clear(); super.destroy(); } } User Registration Model
package com.yourapp.sip; import java.util.Objects; public class UserRegistration { private String username; private String contact; private long registrationTime; private long expires; public UserRegistration(String username, String contact, long registrationTime) { this.username = username; this.contact = contact; this.registrationTime = registrationTime; this.expires = registrationTime + (3600 * 1000); // 1 hour expiry } // Getters and setters public String getUsername() { return username; } public String getContact() { return contact; } public long getRegistrationTime() { return registrationTime; } public long getExpires() { return expires; } public boolean isExpired() { return System.currentTimeMillis() > expires; } public void refresh() { this.expires = System.currentTimeMillis() + (3600 * 1000); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserRegistration that = (UserRegistration) o; return username.equals(that.username); } @Override public int hashCode() { return Objects.hash(username); } @Override public String toString() { return String.format("UserRegistration{username='%s', contact='%s', registered=%d}", username, contact, registrationTime); } } 3. Advanced SIP Server Features
Call Routing and Management
package com.yourapp.sip; import org.mobicents.servlet.sip.RestcommSipServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.sip.*; import java.io.IOException; import java.util.Map; import java.util.concurrent.*; public class AdvancedSipServlet extends RestcommSipServlet { private static final Logger logger = LoggerFactory.getLogger(AdvancedSipServlet.class); private Map<String, CallSession> activeCalls; private ScheduledExecutorService scheduler; private CallRoutingEngine routingEngine; @Override public void init() { this.activeCalls = new ConcurrentHashMap<>(); this.scheduler = Executors.newScheduledThreadPool(10); this.routingEngine = new CallRoutingEngine(); // Start session cleanup task scheduler.scheduleAtFixedRate(this::cleanupExpiredSessions, 1, 1, TimeUnit.MINUTES); logger.info("Advanced SIP Servlet initialized"); } @Override protected void doInvite(SipServletRequest request) throws ServletException, IOException { String callId = request.getCallId(); String from = request.getFrom().getURI().toString(); String to = request.getTo().getURI().toString(); logger.info("New INVITE - Call-ID: {}, From: {}, To: {}", callId, from, to); try { // Create call session CallSession callSession = new CallSession(callId, from, to); activeCalls.put(callId, callSession); // Apply call routing logic RoutingResult routing = routingEngine.routeCall(from, to); if (!routing.isRoutable()) { logger.warn("Call {} cannot be routed", callId); request.createResponse(routing.getRejectCode()).send(); activeCalls.remove(callId); return; } // Send provisional responses request.createResponse(SipServletResponse.SC_TRYING).send(); request.createResponse(SipServletResponse.SC_RINGING).send(); // Store original request for later use callSession.setOriginalRequest(request); // Process call based on routing result processCallRouting(callSession, routing); } catch (Exception e) { logger.error("Error processing INVITE for call {}", callId, e); request.createResponse(SipServletResponse.SC_SERVER_INTERNAL_ERROR).send(); activeCalls.remove(callId); } } private void processCallRouting(CallSession callSession, RoutingResult routing) { switch (routing.getRouteType()) { case DIRECT: processDirectCall(callSession); break; case IVR: processIvrCall(callSession); break; case CONFERENCE: processConferenceCall(callSession); break; case FORWARD: processForwardCall(callSession, routing.getForwardTarget()); break; default: logger.warn("Unknown route type for call {}", callSession.getCallId()); } } private void processDirectCall(CallSession callSession) { scheduler.schedule(() -> { try { SipServletRequest originalRequest = callSession.getOriginalRequest(); SipServletResponse okResponse = originalRequest.createResponse(SipServletResponse.SC_OK); // Set SDP content for media negotiation String sdpContent = createBasicSDP(); okResponse.setContent(sdpContent, "application/sdp"); okResponse.send(); callSession.setStatus(CallStatus.ESTABLISHED); logger.info("Direct call established: {}", callSession.getCallId()); } catch (Exception e) { logger.error("Error establishing direct call {}", callSession.getCallId(), e); callSession.setStatus(CallStatus.FAILED); } }, 2, TimeUnit.SECONDS); } private void processIvrCall(CallSession callSession) { // Implement IVR logic logger.info("Processing IVR call: {}", callSession.getCallId()); // Play welcome message, collect DTMF, etc. } private void processConferenceCall(CallSession callSession) { // Implement conference call logic logger.info("Processing conference call: {}", callSession.getCallId()); } private void processForwardCall(CallSession callSession, String forwardTarget) { try { SipServletRequest originalRequest = callSession.getOriginalRequest(); // Create new INVITE to forward target SipFactory sipFactory = (SipFactory) getServletContext().getAttribute(SIP_FACTORY); SipApplicationSession appSession = sipFactory.createApplicationSession(); SipServletRequest forwardRequest = sipFactory.createRequest( appSession, "INVITE", originalRequest.getFrom(), originalRequest.getTo() ); // Copy headers and content forwardRequest.setContent(originalRequest.getContent(), originalRequest.getContentType()); // Send forward request forwardRequest.send(); callSession.setStatus(CallStatus.FORWARDED); logger.info("Call {} forwarded to {}", callSession.getCallId(), forwardTarget); } catch (Exception e) { logger.error("Error forwarding call {}", callSession.getCallId(), e); callSession.setStatus(CallStatus.FAILED); } } @Override protected void doBye(SipServletRequest request) throws ServletException, IOException { String callId = request.getCallId(); logger.info("BYE received for call: {}", callId); CallSession callSession = activeCalls.remove(callId); if (callSession != null) { callSession.setStatus(CallStatus.TERMINATED); logger.info("Call {} terminated", callId); } request.createResponse(SipServletResponse.SC_OK).send(); } private String createBasicSDP() { return "v=0\r\n" + "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "c=IN IP4 127.0.0.1\r\n" + "t=0 0\r\n" + "m=audio 8000 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n"; } private void cleanupExpiredSessions() { long currentTime = System.currentTimeMillis(); activeCalls.entrySet().removeIf(entry -> entry.getValue().isExpired(currentTime) ); } @Override public void destroy() { scheduler.shutdown(); activeCalls.clear(); logger.info("Advanced SIP Servlet destroyed"); super.destroy(); } } Call Session Management
package com.yourapp.sip; import javax.servlet.sip.SipServletRequest; import java.util.concurrent.atomic.AtomicLong; public class CallSession { private String callId; private String from; private String to; private CallStatus status; private long createTime; private long lastActivity; private SipServletRequest originalRequest; private AtomicLong duration; public CallSession(String callId, String from, String to) { this.callId = callId; this.from = from; this.to = to; this.status = CallStatus.INITIATED; this.createTime = System.currentTimeMillis(); this.lastActivity = createTime; this.duration = new AtomicLong(0); } public enum CallStatus { INITIATED, RINGING, ESTABLISHED, FORWARDED, TERMINATED, FAILED } // Getters and setters public String getCallId() { return callId; } public String getFrom() { return from; } public String getTo() { return to; } public CallStatus getStatus() { return status; } public long getCreateTime() { return createTime; } public long getLastActivity() { return lastActivity; } public long getDuration() { return duration.get(); } public SipServletRequest getOriginalRequest() { return originalRequest; } public void setStatus(CallStatus status) { this.status = status; this.lastActivity = System.currentTimeMillis(); if (status == CallStatus.ESTABLISHED) { // Start duration tracking new Thread(() -> { while (this.status == CallStatus.ESTABLISHED) { try { Thread.sleep(1000); duration.incrementAndGet(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } }).start(); } } public void setOriginalRequest(SipServletRequest request) { this.originalRequest = request; } public boolean isExpired(long currentTime) { // Consider session expired if no activity for 5 minutes return (currentTime - lastActivity) > (5 * 60 * 1000); } public void updateActivity() { this.lastActivity = System.currentTimeMillis(); } @Override public String toString() { return String.format( "CallSession{callId='%s', from='%s', to='%s', status=%s, duration=%ds}", callId, from, to, status, duration.get() ); } } Call Routing Engine
package com.yourapp.sip; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; public class CallRoutingEngine { private Map<String, String> userExtensions; private Map<Pattern, String> routingRules; public CallRoutingEngine() { this.userExtensions = new ConcurrentHashMap<>(); this.routingRules = new ConcurrentHashMap<>(); // Initialize with sample data initializeSampleData(); } private void initializeSampleData() { // User extensions userExtensions.put("1001", "sip:[email protected]"); userExtensions.put("1002", "sip:[email protected]"); userExtensions.put("1003", "sip:[email protected]"); // Routing rules (regex pattern -> action) routingRules.put(Pattern.compile("^1800.*"), "IVR"); routingRules.put(Pattern.compile("^1900.*"), "CONFERENCE"); routingRules.put(Pattern.compile("^\\*70.*"), "CALL_FORWARD"); } public RoutingResult routeCall(String from, String to) { String toUser = extractUsername(to); logger.info("Routing call from {} to {}", from, to); // Check if target is a registered user if (userExtensions.containsKey(toUser)) { return new RoutingResult(RouteType.DIRECT, userExtensions.get(toUser)); } // Apply routing rules for (Map.Entry<Pattern, String> entry : routingRules.entrySet()) { if (entry.getKey().matcher(toUser).matches()) { return applyRoutingRule(entry.getValue(), from, to); } } // Default: reject call return new RoutingResult(false, SipServletResponse.SC_NOT_FOUND, "User not found"); } private RoutingResult applyRoutingRule(String rule, String from, String to) { switch (rule) { case "IVR": return new RoutingResult(RouteType.IVR, null); case "CONFERENCE": return new RoutingResult(RouteType.CONFERENCE, null); case "CALL_FORWARD": String forwardTarget = determineForwardTarget(from); return new RoutingResult(RouteType.FORWARD, forwardTarget); default: return new RoutingResult(false, SipServletResponse.SC_NOT_IMPLEMENTED, "Rule not implemented"); } } private String extractUsername(String sipUri) { // Extract username from SIP URI (sip:user@domain) if (sipUri.contains(":")) { return sipUri.split(":")[1].split("@")[0]; } return sipUri.split("@")[0]; } private String determineForwardTarget(String from) { // Simple forward logic - in real application, this would check user preferences String fromUser = extractUsername(from); return userExtensions.getOrDefault("1002", "sip:[email protected]"); } public void addUserExtension(String extension, String sipUri) { userExtensions.put(extension, sipUri); } public void removeUserExtension(String extension) { userExtensions.remove(extension); } public void addRoutingRule(String pattern, String action) { routingRules.put(Pattern.compile(pattern), action); } } class RoutingResult { private boolean routable; private int rejectCode; private String rejectReason; private RouteType routeType; private String forwardTarget; public RoutingResult(RouteType routeType, String forwardTarget) { this.routable = true; this.routeType = routeType; this.forwardTarget = forwardTarget; } public RoutingResult(boolean routable, int rejectCode, String rejectReason) { this.routable = routable; this.rejectCode = rejectCode; this.rejectReason = rejectReason; } // Getters public boolean isRoutable() { return routable; } public int getRejectCode() { return rejectCode; } public String getRejectReason() { return rejectReason; } public RouteType getRouteType() { return routeType; } public String getForwardTarget() { return forwardTarget; } } enum RouteType { DIRECT, IVR, CONFERENCE, FORWARD, REJECT } 4. SIP Server Configuration
Server Configuration Class
package com.yourapp.sip.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class SipServerConfig { private static final Logger logger = LoggerFactory.getLogger(SipServerConfig.class); private static final String CONFIG_FILE = "/sip-server.properties"; private static SipServerConfig instance; private Properties properties; private SipServerConfig() { loadConfiguration(); } public static synchronized SipServerConfig getInstance() { if (instance == null) { instance = new SipServerConfig(); } return instance; } private void loadConfiguration() { properties = new Properties(); try (InputStream input = getClass().getResourceAsStream(CONFIG_FILE)) { if (input != null) { properties.load(input); logger.info("SIP server configuration loaded successfully"); } else { logger.warn("Configuration file not found, using defaults"); setDefaultProperties(); } } catch (IOException e) { logger.error("Error loading configuration file", e); setDefaultProperties(); } } private void setDefaultProperties() { properties.setProperty("sip.server.host", "127.0.0.1"); properties.setProperty("sip.server.port", "5060"); properties.setProperty("sip.server.transport", "udp"); properties.setProperty("sip.session.timeout", "300"); properties.setProperty("sip.registration.expires", "3600"); properties.setProperty("sip.max.calls", "100"); properties.setProperty("sip.audio.port.range.start", "8000"); properties.setProperty("sip.audio.port.range.end", "8100"); } public String getProperty(String key) { return properties.getProperty(key); } public String getProperty(String key, String defaultValue) { return properties.getProperty(key, defaultValue); } public int getIntProperty(String key, int defaultValue) { try { return Integer.parseInt(properties.getProperty(key)); } catch (NumberFormatException e) { return defaultValue; } } public boolean getBooleanProperty(String key, boolean defaultValue) { String value = properties.getProperty(key); if (value != null) { return Boolean.parseBoolean(value); } return defaultValue; } // Configuration getters public String getServerHost() { return getProperty("sip.server.host", "127.0.0.1"); } public int getServerPort() { return getIntProperty("sip.server.port", 5060); } public String getTransport() { return getProperty("sip.server.transport", "udp"); } public int getSessionTimeout() { return getIntProperty("sip.session.timeout", 300); } public int getMaxCalls() { return getIntProperty("sip.max.calls", 100); } } Configuration Properties File
# sip-server.properties sip.server.host=192.168.1.100 sip.server.port=5060 sip.server.transport=udp sip.server.domain=yourdomain.com # Session settings sip.session.timeout=300 sip.registration.expires=3600 sip.max.calls=100 # Audio settings sip.audio.port.range.start=8000 sip.audio.port.range.end=8100 sip.audio.codec.preference=PCMU,PCMA,G729 # Logging sip.log.level=INFO sip.log.requests=true sip.log.responses=true # Security sip.auth.enabled=true sip.auth.realm=YourSIPRealm sip.auth.nonce.expiry=300 # Performance sip.thread.pool.size=50 sip.queue.size=1000 sip.timer.interval=1000
5. Monitoring and Management
SIP Server Monitor
package com.yourapp.sip.monitor; import com.yourapp.sip.AdvancedSipServlet; import com.yourapp.sip.CallSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.management.*; import java.lang.management.ManagementFactory; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; public class SipServerMonitor implements SipServerMonitorMBean { private static final Logger logger = LoggerFactory.getLogger(SipServerMonitor.class); private AdvancedSipServlet sipServlet; private AtomicLong totalCalls; private AtomicLong successfulCalls; private AtomicLong failedCalls; private AtomicLong totalRegistrations; public SipServerMonitor(AdvancedSipServlet sipServlet) { this.sipServlet = sipServlet; this.totalCalls = new AtomicLong(0); this.successfulCalls = new AtomicLong(0); this.failedCalls = new AtomicLong(0); this.totalRegistrations = new AtomicLong(0); registerMBean(); } private void registerMBean() { try { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); ObjectName name = new ObjectName("com.yourapp.sip:type=SipServerMonitor"); mbs.registerMBean(this, name); logger.info("SIP Server Monitor MBean registered successfully"); } catch (Exception e) { logger.error("Failed to register MBean", e); } } // MBean interface implementation @Override public long getTotalCalls() { return totalCalls.get(); } @Override public long getSuccessfulCalls() { return successfulCalls.get(); } @Override public long getFailedCalls() { return failedCalls.get(); } @Override public long getActiveCalls() { return sipServlet.getActiveCalls().size(); } @Override public long getTotalRegistrations() { return totalRegistrations.get(); } @Override public int getRegisteredUsers() { return sipServlet.getRegisteredUsers().size(); } @Override public double getCallSuccessRate() { long total = totalCalls.get(); if (total == 0) return 0.0; return (successfulCalls.get() * 100.0) / total; } @Override public String getServerStatus() { return "RUNNING"; } @Override public Map<String, CallSession> getActiveCallDetails() { return sipServlet.getActiveCalls(); } @Override public void resetStatistics() { totalCalls.set(0); successfulCalls.set(0); failedCalls.set(0); totalRegistrations.set(0); } // Methods to update statistics public void incrementTotalCalls() { totalCalls.incrementAndGet(); } public void incrementSuccessfulCalls() { successfulCalls.incrementAndGet(); } public void incrementFailedCalls() { failedCalls.incrementAndGet(); } public void incrementRegistrations() { totalRegistrations.incrementAndGet(); } } // MBean Interface public interface SipServerMonitorMBean { long getTotalCalls(); long getSuccessfulCalls(); long getFailedCalls(); long getActiveCalls(); long getTotalRegistrations(); int getRegisteredUsers(); double getCallSuccessRate(); String getServerStatus(); Map<String, CallSession> getActiveCallDetails(); void resetStatistics(); } 6. Deployment and Testing
Main Application Class
package com.yourapp.sip; import org.mobicents.servlet.sip.startup.SipServletContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletContextEvent; public class SipServerApplication extends SipServletContextListener { private static final Logger logger = LoggerFactory.getLogger(SipServerApplication.class); @Override public void contextInitialized(ServletContextEvent sce) { logger.info("Starting Mobicents SIP Server Application"); try { // Initialize configuration SipServerConfig config = SipServerConfig.getInstance(); // Log startup information logger.info("SIP Server Configuration:"); logger.info(" Host: {}", config.getServerHost()); logger.info(" Port: {}", config.getServerPort()); logger.info(" Transport: {}", config.getTransport()); logger.info(" Max Calls: {}", config.getMaxCalls()); super.contextInitialized(sce); logger.info("Mobicents SIP Server Application started successfully"); } catch (Exception e) { logger.error("Failed to start SIP Server Application", e); throw new RuntimeException("SIP Server initialization failed", e); } } @Override public void contextDestroyed(ServletContextEvent sce) { logger.info("Stopping Mobicents SIP Server Application"); try { super.contextDestroyed(sce); logger.info("Mobicents SIP Server Application stopped successfully"); } catch (Exception e) { logger.error("Error during SIP Server shutdown", e); } } } Key Features Covered:
- Basic SIP Servlet - Handling REGISTER, INVITE, BYE requests
- Call Management - Session tracking and call state management
- Call Routing - Advanced routing logic with multiple route types
- Configuration Management - External configuration support
- Monitoring - JMX monitoring and statistics
- User Registration - SIP user management and authentication
This comprehensive Mobicents SIP server implementation provides a solid foundation for building VoIP applications, call centers, IVR systems, and other real-time communication solutions.