/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.smscserver.impl;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.smscserver.MessageObserver;
import org.apache.smscserver.ServerSmscStatistics;
import org.apache.smscserver.StatisticsObserver;
import org.apache.smscserver.smsclet.SmscIoSession;
import org.apache.smscserver.smsclet.SmscReply;
import org.apache.smscserver.smsclet.SmscRequest;
import org.apache.smscserver.smsclet.User;
/**
* <strong>Internal class, do not use directly.</strong>
*
* This is SMSC statistics implementation.
*
* TODO revisit concurrency, right now we're a bit over zealous with both Atomic* counters and synchronization
*
* @author hceylan
*/
public class DefaultSmscStatistics implements ServerSmscStatistics {
private static class UserBindss {
private final Map<InetAddress, AtomicInteger> perAddress = new ConcurrentHashMap<InetAddress, AtomicInteger>();
public AtomicInteger totalBinds;
public UserBindss(InetAddress address) {
// init with the first connection
this.totalBinds = new AtomicInteger(1);
this.perAddress.put(address, new AtomicInteger(1));
}
public AtomicInteger bindsFromInetAddress(InetAddress address) {
AtomicInteger binds = this.perAddress.get(address);
if (binds == null) {
binds = new AtomicInteger(0);
this.perAddress.put(address, binds);
}
return binds;
}
}
private StatisticsObserver observer = null;
private MessageObserver messageObserver = null;
private Date startTime = new Date();
private final AtomicInteger messageReceivedCount = new AtomicInteger(0);
private final AtomicInteger messageSentCount = new AtomicInteger(0);
private final AtomicInteger currBinds = new AtomicInteger(0);
private final AtomicInteger totalBinds = new AtomicInteger(0);
private final AtomicInteger totalFailedBinds = new AtomicInteger(0);
private final AtomicInteger currConnections = new AtomicInteger(0);
private final AtomicInteger totalConnections = new AtomicInteger(0);
/**
* The user bind information.
*/
private final Map<String, UserBindss> userBindTable = new ConcurrentHashMap<String, UserBindss>();
public static final String BIND_NUMBER = "bind_number";
/**
* Get current number of binds.
*/
public int getCurrentBindNumber() {
return this.currBinds.get();
}
/**
* Get current number of connections.
*/
public int getCurrentConnectionNumber() {
return this.currConnections.get();
}
/**
* Get the bind number for the specific user
*/
public synchronized int getCurrentUserBindNumber(final User user) {
if (this.userBindTable.get(user.getName()) == null) {// not found the bind user's statistics info
return 0;
} else {
return this.userBindTable.get(user.getName()).totalBinds.get();
}
}
/**
* Get the bind number for the specific user from the ipAddress
*
* @param user
* bind user account
* @param ipAddress
* the ip address of the remote user
*/
public synchronized int getCurrentUserBindNumber(final User user, final InetAddress ipAddress) {
if (this.userBindTable.get(user.getName()) == null) {// not found the bind user's statistics info
return 0;
} else {
return this.userBindTable.get(user.getName()).bindsFromInetAddress(ipAddress).get();
}
}
/**
* Get server start time.
*/
public Date getStartTime() {
if (this.startTime != null) {
return (Date) this.startTime.clone();
} else {
return null;
}
}
/**
* Get total number of binds.
*/
public int getTotalBindNumber() {
return this.totalBinds.get();
}
/**
* Get total number of connections.
*/
public int getTotalConnectionNumber() {
return this.totalConnections.get();
}
/**
* Get total failed bind number.
*/
public int getTotalFailedBindNumber() {
return this.totalFailedBinds.get();
}
/**
* Get number of messages received.
*/
public int getTotalMessageReceivedNumber() {
return this.messageReceivedCount.get();
}
/**
* Get number of messages sent.
*/
public int getTotalMessageSentNumber() {
return this.messageReceivedCount.get();
}
/**
* Observer bind notification.
*/
private void notifyBind(final SmscIoSession session) {
if (this.observer != null) {
this.observer.notifyBind();
}
}
/**
* Observer failed bind notification.
*/
private void notifyBindFail(final SmscIoSession session) {
StatisticsObserver observer = this.observer;
if (observer != null) {
if (session.getRemoteAddress() instanceof InetSocketAddress) {
observer.notifyBindFail(((InetSocketAddress) session.getRemoteAddress()).getAddress());
}
}
}
/**
* Observer close connection notification.
*/
private void notifyCloseConnection(final SmscIoSession session) {
if (this.observer != null) {
this.observer.notifyCloseConnection();
}
}
/**
* Observer message received notification.
*/
private void notifyMessageReceived(final SmscIoSession session, final SmscRequest request) {
if (this.observer != null) {
this.observer.notifyMessageReceived();
}
if (this.messageObserver != null) {
this.messageObserver.notifyMessageReceived(session, request);
}
}
/**
* Observer message sent notification.
*/
private void notifyMessageSent(final SmscIoSession session, final SmscReply reply) {
if (this.observer != null) {
this.observer.notifyMessageSent();
}
if (this.messageObserver != null) {
this.messageObserver.notifyMessageSent(session, reply);
}
}
/**
* Observer open connection notification.
*/
private void notifyOpenConnection(final SmscIoSession session) {
if (this.observer != null) {
this.observer.notifyOpenConnection();
}
}
/**
* Observer unbind notification.
*/
private void notifyUnbind(final SmscIoSession session) {
if (this.observer != null) {
this.observer.notifyUnbind();
}
}
/**
* Reset the cumulative counters.
*/
public synchronized void resetStatisticsCounters() {
this.startTime = new Date();
this.messageReceivedCount.set(0);
this.messageSentCount.set(0);
this.totalBinds.set(0);
this.totalFailedBinds.set(0);
this.totalConnections.set(0);
}
/**
* New bind.
*/
public synchronized void setBind(final SmscIoSession session) {
this.currBinds.incrementAndGet();
this.totalBinds.incrementAndGet();
User user = session.getUser();
synchronized (user) {// thread safety is needed. Since the bind occurs
// at low frequency, this overhead is endurable
UserBindss statisticsTable = this.userBindTable.get(user.getName());
if (statisticsTable == null) {
// the hash table that records the bind information of the user
// and its ip address.
InetAddress address = null;
if (session.getRemoteAddress() instanceof InetSocketAddress) {
address = ((InetSocketAddress) session.getRemoteAddress()).getAddress();
}
statisticsTable = new UserBindss(address);
this.userBindTable.put(user.getName(), statisticsTable);
} else {
statisticsTable.totalBinds.incrementAndGet();
if (session.getRemoteAddress() instanceof InetSocketAddress) {
InetAddress address = ((InetSocketAddress) session.getRemoteAddress()).getAddress();
statisticsTable.bindsFromInetAddress(address).incrementAndGet();
}
}
}
this.notifyBind(session);
}
/**
* Increment failed bind count.
*/
public synchronized void setBindFail(final SmscIoSession session) {
this.totalFailedBinds.incrementAndGet();
this.notifyBindFail(session);
}
/**
* Decrement open connection count.
*/
public synchronized void setCloseConnection(final SmscIoSession session) {
if (this.currConnections.get() > 0) {
this.currConnections.decrementAndGet();
}
this.notifyCloseConnection(session);
}
/**
* Set the message observer.
*/
public void setMessageObserver(final MessageObserver observer) {
this.messageObserver = observer;
}
/**
* Increment message received count.
*/
public synchronized void setMessageReceived(final SmscIoSession session, final SmscRequest request) {
this.messageReceivedCount.incrementAndGet();
this.notifyMessageReceived(session, request);
}
/**
* Increment download count.
*/
public synchronized void setMessageSent(final SmscIoSession session, final SmscReply reply) {
this.messageSentCount.incrementAndGet();
this.notifyMessageSent(session, reply);
}
/**
* Set the observer.
*/
public void setObserver(final StatisticsObserver observer) {
this.observer = observer;
}
/**
* Increment open connection count.
*/
public synchronized void setOpenConnection(final SmscIoSession session) {
this.currConnections.incrementAndGet();
this.totalConnections.incrementAndGet();
this.notifyOpenConnection(session);
}
/**
* User unbind
*/
public synchronized void setUnbind(final SmscIoSession session) {
User user = session.getUser();
if (user == null) {
return;
}
this.currBinds.decrementAndGet();
synchronized (user) {
UserBindss userBinds = this.userBindTable.get(user.getName());
if (userBinds != null) {
userBinds.totalBinds.decrementAndGet();
if (session.getRemoteAddress() instanceof InetSocketAddress) {
InetAddress address = ((InetSocketAddress) session.getRemoteAddress()).getAddress();
userBinds.bindsFromInetAddress(address).decrementAndGet();
}
}
}
this.notifyUnbind(session);
}
}