/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2016, TeleStax Inc. and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* JBoss, Home of Professional Open Source
* Copyright 2007-2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jdiameter.server.impl;
import static org.jdiameter.client.impl.helpers.Parameters.AcctApplId;
import static org.jdiameter.client.impl.helpers.Parameters.ApplicationId;
import static org.jdiameter.client.impl.helpers.Parameters.AuthApplId;
import static org.jdiameter.client.impl.helpers.Parameters.VendorId;
import static org.jdiameter.server.impl.helpers.Parameters.OverloadEntryIndex;
import static org.jdiameter.server.impl.helpers.Parameters.OverloadEntryhighThreshold;
import static org.jdiameter.server.impl.helpers.Parameters.OverloadEntrylowThreshold;
import static org.jdiameter.server.impl.helpers.Parameters.OverloadMonitor;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.jdiameter.api.ApplicationId;
import org.jdiameter.api.Configuration;
import org.jdiameter.api.OverloadListener;
import org.jdiameter.api.URI;
import org.jdiameter.server.api.IOverloadManager;
/**
*
* @author erick.svenson@yahoo.com
* @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
* @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
*/
public class OverloadManagerImpl implements IOverloadManager {
private ConcurrentLinkedQueue<OverloadInfo> listeners = new ConcurrentLinkedQueue<OverloadInfo>();
private ConcurrentHashMap<Object, AppOverloadInfo> appInfo = new ConcurrentHashMap<Object, AppOverloadInfo>();
public OverloadManagerImpl(Configuration config) {
Configuration[] entries = config.getChildren(OverloadMonitor.ordinal());
if (entries == null) {
return;
}
for (Configuration e : entries) {
ApplicationId appId = null;
Configuration[] cAppId = e.getChildren(ApplicationId.ordinal());
for (Configuration i : cAppId) {
if ( i.getLongValue(AuthApplId.ordinal(), 0) != 0 ) {
appId = org.jdiameter.api.ApplicationId.createByAuthAppId(
i.getLongValue(VendorId.ordinal(), 0),
i.getLongValue(AuthApplId.ordinal(), 0)
);
}
else {
appId = org.jdiameter.api.ApplicationId.createByAccAppId(
i.getLongValue(VendorId.ordinal(), 0),
i.getLongValue(AcctApplId.ordinal(), 0)
);
}
break;
}
if (appId == null) {
continue;
}
AppOverloadInfo info = new AppOverloadInfo(appId);
info.appendEntry(
e.getIntValue(OverloadEntryIndex.ordinal(), 0),
e.getDoubleValue(OverloadEntrylowThreshold.ordinal(), 0),
e.getDoubleValue(OverloadEntryhighThreshold.ordinal(), 0)
);
appInfo.put(appId, info);
}
}
@Override
public void parentAppOverloadDetected(ApplicationId applicationId, int type, double value) {
AppOverloadInfo app = appInfo.get(createKey(applicationId));
if (app != null) {
app.updateInformation(type, value);
}
}
@Override
public void parentAppOverloadCeased(ApplicationId applicationId, int type) {
AppOverloadInfo app = appInfo.get(createKey(applicationId));
if (app != null) {
app.updateInformation(type, 0);
}
}
private Object createKey(final ApplicationId appId) {
return new Object() {
@Override
public int hashCode() {
return appId.hashCode();
}
@Override
public boolean equals(Object obj) {
return appId.equals(obj);
}
};
}
@Override
public boolean isParenAppOverload(final ApplicationId appId) {
if (appId == null) {
return false;
}
AppOverloadInfo app = appInfo.get( createKey(appId) );
return app != null && app.isOverload();
}
@Override
public boolean isParenAppOverload(final ApplicationId appId, final int type) {
AppOverloadInfo app = appInfo.get( createKey(appId) );
return app != null && app.isOverload(type);
}
@Override
public void addOverloadListener(OverloadListener overloadListener, double lowThreshold, double highThreshold, int qIndex) {
listeners.add(new OverloadInfo(overloadListener, lowThreshold, highThreshold, qIndex));
}
@Override
public void removeOverloadListener(OverloadListener overloadListener, int qIndex) {
listeners.remove(new OverloadInfo(overloadListener, qIndex));
}
@Override
public void changeNotification(int index, URI uri, double value) {
for (OverloadInfo e : listeners) {
if (e.getCode() == index) {
e.changeNotification(uri, value);
}
}
}
public static class AppOverloadInfo {
private ApplicationId appId;
private ArrayList <AppOverloadInfoEntry> entries = new ArrayList<AppOverloadInfoEntry>();
private final Object lock = new Object();
public ApplicationId getAppId() {
return appId;
}
public AppOverloadInfo(ApplicationId appId) {
this.appId = appId;
}
public void appendEntry(int type, double lowThreshold, double highThreshold) {
entries.add(new AppOverloadInfoEntry(type, lowThreshold, highThreshold));
}
public boolean isOverload() {
for (AppOverloadInfoEntry e : entries) {
if (e.isOverload()) {
return true;
}
}
return false;
}
public boolean isOverload(int type) {
for (AppOverloadInfoEntry e : entries) {
if (e.getType() == type) {
synchronized (lock) {
if (e.isOverload()) {
return true;
}
}
}
}
return false;
}
public void updateInformation(int type, double threshold) {
for (AppOverloadInfoEntry e : entries) {
if (e.getType() == type) {
synchronized (lock) {
e.updateInformation(threshold);
}
}
}
}
}
public static class AppOverloadInfoEntry {
private int type;
private double lowThreshold, highThreshold;
private double currentValue;
private final Object lock = new Object();
public AppOverloadInfoEntry(int type, double lowThreshold, double highThreshold) {
this.type = type;
this.lowThreshold = lowThreshold;
this.highThreshold = highThreshold;
}
public int getType() {
return type;
}
public double getLowThreshold() {
return lowThreshold;
}
public double getHighThreshold() {
return highThreshold;
}
public double getCurrentValue() {
return currentValue;
}
public void updateInformation(double threshold) {
synchronized (lock) {
this.currentValue = threshold;
}
}
public boolean isOverload() {
synchronized (lock) {
return (currentValue >= lowThreshold && currentValue <= highThreshold);
}
}
}
public static class OverloadInfo {
private OverloadListener overloadListener;
private double lowThreshold, highThreshold;
private int qIndex;
private boolean isOverload;
private Lock lock = new ReentrantLock();
public OverloadInfo(OverloadListener overloadListener, int qIndex) {
this.overloadListener = overloadListener;
this.qIndex = qIndex;
}
public OverloadInfo(OverloadListener overloadListener, double lowThreshold, double highThreshold, int qIndex) {
this.overloadListener = overloadListener;
this.lowThreshold = lowThreshold;
this.highThreshold = highThreshold;
this.qIndex = qIndex;
}
public void changeNotification(URI uri, double value) {
if ( value >= lowThreshold && value <= highThreshold ) {
overloadListener.overloadDetected(uri, value);
lock.lock();
isOverload = true;
lock.unlock();
} else {
lock.lock();
if (isOverload) {
overloadListener.overloadCeased(uri);
isOverload = false;
}
lock.unlock();
}
}
public int getCode() {
return qIndex;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OverloadInfo that = (OverloadInfo) o;
if (qIndex != that.qIndex) {
return false;
}
if (overloadListener != null ? !overloadListener.equals(that.overloadListener) : that.overloadListener != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result;
result = (overloadListener != null ? overloadListener.hashCode() : 0);
result = 31 * result + qIndex;
return result;
}
}
}